Highcharts: Красивые, динамические графики за 5 минут на Laravel

Highcharts — библиотека для создания графиков написанная на JavaScript, позволяет легко добавлять интерактивные, анимированные графики на сайт или в веб-приложение. На данный момент графики поддерживают большое количество диаграмм линейных, круговых, колоночных рассеивающих и многих других типов.

Графики работают со всеми популярными браузерами, включая Safari на iPhone.

В этой статье мы рассмотрим простой вариант использования графика изменения соотношения стоимости пары Usd/Eur. Я рассчитываю, что у вас уже есть установленный и настроенный Laravel.

p.s. Статья получилась с сильным уклоном на backend чем на frontend, т.к. по ссылке выше можно легко копипастнуть код вывода графиков. Но получение данных, там не описано

А начнем мы с описания нашей задачи.

В первую очередь, нам необходимо иметь возможность получать информацию о котировках валют. Для этого мы можем использовать данные из файла, брать данные по сети, из базы данных или как либо еще. Например, из мемкеша. Затем нам необходимо будет обработать эти данные и отдать в представление для показа пользователю.

Во-вторых, наши графики могут строиться не только в одном конкретном контроллере, но и в разных частях нашего приложения. Потому, чтобы не дублировать один и тот же код и соблюдать принципы DRY, получение данных валют мы вынесем в отдельное абстрактное место. В отдельный класс – репозиторий.

Для начала, давайте опишем интерфейс нашего репозитория. Создадим новый файл контракта:

<?php
declare(strict_types=1);

namespace App\Contracts;

use Illuminate\Support\Collection;

interface ExchangeRateRepositoryContract
{
    public function getData(): Collection;
}

Судя по этому интерфейсу, все наши репозитории обязаны будут иметь метод getData, который возвращает нам некую коллекцию.

Создадим наш первый репозиторий:

<?php
declare(strict_types=1);

namespace App\Repositories;

use App\Contracts\ExchangeRateRepositoryContract;
use Illuminate\Support\Collection;

class ExchangeRatesFromUrlRepository implements ExchangeRateRepositoryContract
{
    public function getData(): Collection
    {
        $data = file_get_contents('https://cdn.jsdelivr.net/gh/highcharts/highcharts@v7.0.0/samples/data/usdeur.json');
        $json = json_decode($data, true);

        return new Collection($json);
    }
}

Все довольно просто. Данный репозиторий, как следует из его названия, берет данные по какому-то url, декодирует как-то и возвращает нам коллекцию.

В файле AppServiceProvider.php забиндим текущий контракт с нашей реализацией

public function register()
    {
        $this->app->bind(ExchangeRateRepositoryContract::class, ExchangeRatesFromUrlRepository::class);
    }

Теперь давайте создадим новый контроллер, назовем его ChartController:

sail artisan make:controller ChartController

и пропишем ему зависимости в конструкторе

<?php

namespace App\Http\Controllers;

use App\Contracts\ExchangeRateRepositoryContract;
use Illuminate\Http\Request;

class ChartController extends Controller
{
    private ExchangeRateRepositoryContract $exchangeRateRepository;

    public function __construct(ExchangeRateRepositoryContract $exchangeRateRepository)
    {
        $this->exchangeRateRepository = $exchangeRateRepository;
    }

    public function index(Request $request)
    {
        $data = json_encode($this->exchangeRateRepository->getData()->toArray());

        return view('chart.index' , compact('data'));
    }
}

В методе index нам необходимо получить данные из нашего репозитория, конвертировать в json и передать в представление.

В самом представлении нам понадобятся библиотеки самого Highcharts

<script src="https://code.highcharts.com/highcharts.js"></script>
    <script src="https://code.highcharts.com/modules/data.js"></script>
    <script src="https://code.highcharts.com/modules/exporting.js"></script>
    <script src="https://code.highcharts.com/modules/export-data.js"></script>
    <script src="https://code.highcharts.com/modules/accessibility.js"></script>

контейнер для графика

<div id="container"></div>

ну и сам код графика. Пример взят со страницы демо

Highcharts.chart('container', {
                    title: {
                        text: 'USD to EUR exchange rate over time'
                    },
                    xAxis: {
                        type: 'datetime'
                    },
                    yAxis: {
                        title: {
                            text: 'Exchange rate'
                        }
                    },
                    series: [{
                        type: 'area',
                        name: 'USD to EUR',
                        data: {{ $data }}
                    }]
                });

Теперь можно посмотреть, что у нас получилось:

На следующий день, мы узнаем, что путь, по которому мы получали данные больше не существует. Нагрузка от нас на сервер оказалась высокой и нас попросили так больше не делать. Что делать? Пишем крон задачу, которая раз в сутки будет запрашивать данные с удаленного сервера и записывать данные в файл.

Затем создадим новый репозиторий, который будет читать уже из файла:

<?php
declare(strict_types=1);

namespace App\Repositories;

use App\Contracts\ExchangeRateRepositoryContract;
use Illuminate\Support\Collection;

class ExchangeRatesFromFileRepository implements ExchangeRateRepositoryContract
{
    public function getData(): Collection
    {
        $filePath = storage_path('app/data.json');

        $data = file_get_contents($filePath);
        $json = json_decode($data, true);

        return new Collection($json);
    }
}

и в сервис провайдере лишь заменим строчку c ExchangeRatesFromUrlRepository на ExchangeRatesFromFileRepository

public function register()
    {
        $this->app->bind(ExchangeRateRepositoryContract::class, ExchangeRatesFromFileRepository::class);
    }

Так как в контроллерах мы сделали связь на интерфейс, то править там ничего не придется. Строчка выше автоматически применится ко всем контроллерам, которые запрашивают данный контракт ExchangeRateRepositoryContract. Такой подход описан в принципе Открытости/Закрытости в SOLID

Если однажды, вам потребуется получать данные из бд, вы просто напишете новый репозиторий ExchangeRatesFromDbRepository, опишите в нем новую логику получения данных и поменяете строчку в AppServiceProvider

Рейтинг
( 1 оценка, среднее 5 из 5 )
Максим Гречушников/ автор статьи
Мне 40 лет, и больше половины жизни я занимаюсь разработкой. За это время я работал с корпоративными системами, highload-проектами, интеграциями, платежными решениями, внутренними платформами и legacy-кодом, который пережил не одного разработчика. Некоторые системы, кажется, переживут и нас. Моя специализация — PHP, Laravel, backend-архитектура, DDD, тестирование, рефакторинг и техническое лидерство. Мне интересны задачи, где нужно соединить код, бизнес, данные, процессы и здравый смысл. Последние годы я много работаю с LLM и AI-инструментами: использую их для проектирования, анализа кода, документации, тестов, ревью, работы с legacy и построения инженерных процессов. Для меня LLM — это не замена разработчику, а усилитель для тех, кто умеет думать системно. В блоге я пишу о Laravel, PHP, архитектуре, тестировании, инженерной практике, управлении разработкой и реальном применении AI в работе программиста.
Понравилась статья? Поделиться с друзьями: