Как создать пользовательский сборщик данных

Профилировшик Symfony делегирует сбор данных некоторым особенным классам, которые называются сборщиками данных. Symfony поставляется с несколькими встроенными, но вы можете с лёгкостью создать собственный.

Создание пользовательского сборщика данных

Создание пользовательского сборщика данных заключается просто в реализации DataCollectorInterface:

1
2
3
4
5
interface DataCollectorInterface
{
    function collect(Request $request, Response $response, \Exception $exception = null);
    function getName();
}

Метод getName() возвращает имя сборщика данных и долежн быть уникальным в приложении. Значение также используется для получения доступа к информации позже (см., например, How to Use the Profiler in a Functional Test).

Метод collect() отвечает за хранение собранных данных в локальных свойствах.

Caution

Метод collect() вызывается только единожды. Он не используется для "сбора" данных, но существует для "подбирания" данных, которые были сохранены вашим сервисом.

В большинстве случаев, удобно расширять класс DataCollector и наполнять свойство $this->data (оно заботится о сериализации свойства $this->data). Представьте, что вы создаёте новый сборщик данных, который собирает метод и приемлемые типы содержимого из запроса:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// src/AppBundle/DataCollector/RequestCollector.php
namespace AppBundle\DataCollector;

use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class RequestCollector extends DataCollector
{
    public function collect(Request $request, Response $response, \Exception $exception = null)
    {
        $this->data = array(
            'method' => $request->getMethod(),
            'acceptable_content_types' => $request->getAcceptableContentTypes(),
        );
    }

    public function getMethod()
    {
        return $this->data['method'];
    }

    public function getAcceptableContentTypes()
    {
        return $this->data['acceptable_content_types'];
    }

    public function getName()
    {
        return 'app.request_collector';
    }
}

Геттеры добавляются для того, чтобы дать шаблону доступ к собранной информации.

Caution

Если данные не связаны напрямую с запросом или ответом, вам нужно сделать их доступными для вашего DataCollector. Этого можно достичь путём внедрения сервиса, содержащего информацию, которую вы хотите проифлировать в ваш DataCollector.

Caution

Так как профилировщик сериализует экземпляры сборщика данных, вам не стоит хранить объекты, которые не поддаются сериализации (как, например, PDO объекты) или вам нужно будет предоставить ваш собственный метод serialize().

Включение пользовательских сборщиков данных

Если вы используете конфигурацию services.yml по умолчанию с autoconfigure, то Symfony автоматически заметит ваш новый сборщик данных! Ваш метод collect() должен быть вызван при последующем обновлении.

Note

Если вы не используете autoconfigure, то вы также можете вручную смонтировать ваш сервис и тегировать его с помощью data_collector.

Добавление шаблонов веб-профилировщика

Информация, собранная вашим сборщиком данных, может быть отображена как в панели инструментов веб-отладки, так и в веб-профилировщике. Чтобы сделать это, вам понадобится создать шаблон Twig, включающий некоторые конкретные блоки.

В наипростейшем случае, вам просто нужно отобразить информацию в панели инструментов, не предоставляя панели профилировщика. Это требует определения блока toolbar и установки значений двух переменных, под названием icon и text:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{% extends 'WebProfilerBundle:Profiler:layout.html.twig' %}

{% block toolbar %}
    {% set icon %}
        {# это содержимое, отображённое в виде панели в панели инструментов #}
        <span class="icon"><img src="..." alt=""/></span>
        <span class="sf-toolbar-status">Request</span>
    {% endset %}

    {% set text %}
        {# это содержимое, отображённое при наведении мыши на панель инструментов #}
        <div class="sf-toolbar-info-piece">
            <b>Method</b>
            <span>{{ collector.method }}</span>
        </div>

        <div class="sf-toolbar-info-piece">
            <b>Accepted content type</b>
            <span>{{ collector.acceptableContentTypes|join(', ') }}</span>
        </div>
    {% endset %}

    {# значение 'link' установленное, как 'false', означает, что эта панель не
       отображает раздел в веб-профилировщике #}
    {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: false }) }}
{% endblock %}

Tip

Встроенные шаблоны сборщиков определяют свои изображения, как встроенные изображения, закодированные в base64. С помощью этого они могут работать везде, не путаясь со ссылками веб-ресурсов:

1
<img src="data:image/png;base64,..." />

Ещё одно решение - определить изображения, как SVG-файлы. Кроме того, что они не зависят от разрешения, эти изображения могут быть с лёгкостью встроены в шаблон Twig или подключены из внешнего файла для повторного использования в нескольких шаблонах:

1
{{ include('@App/data_collector/icon.svg') }}

Использование последней техники в ваших собственны панелях инструментов поощряется.

Если панель инструментов включает расширенную информауию веб-профилировщика, то шаблон Twig должен также определять дополнительные блоки:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{% extends '@WebProfiler/Profiler/layout.html.twig' %}

{% block toolbar %}
    {% set icon %}
        <span class="icon"><img src="..." alt=""/></span>
        <span class="sf-toolbar-status">Request</span>
    {% endset %}

    {% set text %}
        <div class="sf-toolbar-info-piece">
            {# ... #}
        </div>
    {% endset %}

    {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }}
{% endblock %}

{% block head %}
    {# Необязательно. Тут вы можете дать ссылку или определить ваше собственное
       CSS и JS содержимое. #}
    {# Используйте {{ parent() }}, чтобы расширить стили по умолчанию, вместо того,
       чтобы переопределять их. #}
{% endblock %}

{% block menu %}
    {# Это левостороннее меню отображается при использовании полноэкранного
       профилировщика. #}
    <span class="label">
        <span class="icon"><img src="..." alt=""/></span>
        <strong>Request</strong>
    </span>
{% endblock %}

{% block panel %}
    {# Необязательно для отображения большинства деталей. #}
    <h2>Acceptable Content Types</h2>
    <table>
        <tr>
            <th>Content Type</th>
        </tr>

        {% for type in collector.acceptableContentTypes %}
        <tr>
            <td>{{ type }}</td>
        </tr>
        {% endfor %}
    </table>
{% endblock %}

Блоки menu и panel являются единственными обязательными блоками для определения содержимого, отбражённого в панели веб-профилировщика, связанного с этим сборщиком данных. Все блоки имеют доступ к объекту collector.

Наконец, чтобы включить шаблон сборщика данных, переопределите конфигурацию вашего сервиса, чтобы указать тег, содержащий шаблон:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    # app/config/services.yml
    services:
        AppBundle\DataCollector\RequestCollector:
            tags:
                -
                    name:     data_collector
                    template: 'data_collector/template.html.twig'
                    # должно совпадать со значением, возвращённым методом getName()
                    id:       'app.request_collector'
                    # необязательная приоритетность
                    # priority: 300
            public: false
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- app/config/services.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="AppBundle\DataCollector\RequestCollector" public="false">
                <tag name="data_collector"
                    template="data_collector/template.html.twig"
                    id="app.request_collector"
                    <!-- priority="300" -->
                />
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    // app/config/services.php
    use AppBundle\DataCollector\RequestCollector;
    
    $container
        ->autowire(RequestCollector::class)
        ->setPublic(false)
        ->addTag('data_collector', array(
            'template' => 'data_collector/template.html.twig',
            'id'       => 'app.request_collector',
            // 'priority' => 300,
        ))
    ;
    

Положение каждой панели в панели инструментов определяется приоритетом, определённым каждым сборщиком. Большинство встроенных сборщиков используют 255 в качестве своего приоритета. Если вы хотите, чтобы сборщик отображался раньше, используйте более высокое значение (например, 300).

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.