Zend Framework 2 — Приложения/Модули/Менеджеры служб – О боже

Я только начал изучать Zend Framework 2 в качестве давнего разработчика Zend Framework 1. У меня небольшая проблема с новой терминологией.

Вернувшись в ZF1, если бы я хотел создать регистратор, который был бы глобальным для приложения, я бы добавил конфигурацию в файл application.ini, и загрузчик инициализировал бы его как ресурс (надеюсь, я правильно говорю). Таким образом, с любого из моих контроллеров модулей я мог получить доступ к регистратору через ресурсы начальной загрузки.

Войдите в ZF2, модули — это немного другой зверь, они автономны, но я немного смущен тем, как они взаимодействуют с приложением. Мне кажется, что именно здесь в игру вступает ServiceManager. Моя цель состоит в том, чтобы иметь мой модуль (не контроллер, а сам модуль), чтобы проверить, определило ли приложение регистратор, и если да, использовать этот регистратор во всем модуле. Если приложение не определяет регистратор, я хочу, чтобы модуль определял регистратор для ведения журнала в масштабе модуля.

Этот вопрос также относится к базам данных, скажем, я хочу, чтобы приложение определяло логику подключения к базе данных, в то время как я хочу, чтобы модуль определял логику необходимых ему таблиц. Как именно мне это настроить и как/где я могу узнать, есть ли уже ресурс базы данных, определенный в приложении.

Примечание. Я просмотрел «Быстрый старт» Роба Аллена (полная информация и единственный найденный мной ресурс, в котором до сих пор отсутствует неизвестность), и ZF2 (readthedocs), и уже много гуглил. Что я обнаружил, так это то, что информация, как правило, очень неясна, когда речь идет о том, «куда» идут определенные части головоломки.


person Aaron Murray    schedule 14.02.2013    source источник


Ответы (2)


Что вы знаете из Zend Framework 1.x, так это «Ресурс приложения».

Понятие «ресурс приложения» заменено в Zend Framework 2 так называемыми "сервисами" (введение здесь)

Еще одно изменение касается самих модулей. В ZF1 модуль был в основном подразделом вашего приложения, который обрабатывал некоторые запросы. В ZF2 это уже не так: если ваш модуль определяет службу или контроллер, он теперь доступен для всех приложений. Есть хорошее введение о некоторых различиях между ZF1 и ZF2 от Гэри Хокина.

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

Для вашего конкретного случая регистратора я предлагаю, чтобы ваш модуль всегда определял регистратор и использовал его. Что можно сделать, чтобы определить регистратор условно, следующее:

class MyModule
{
    public function onBootstrap($e)
    {
        // $e->getTarget() is the \Zend\Mvc\Application
        $sm = $e->getTarget()->getServiceManager();

        if (!$sm->has('some-logger-name')) {
            $sm->setFactory('some-logger-name', function ($sl) {
                return new MyLogger($sl->get('some-db'));
            });
        }
    }
}

После этого вы сможете использовать свое «имя регистратора» во всех приложениях.

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

class MyModule
{
    public function getConfig()
    {
        return array(
            'service_manager' => array(
                'factories' => array(
                    'some-logger-name' => 'My\Logger\Factory\ClassName'
                ),
            ),
        );
    }
}

То же самое достигается с помощью getServiceConfig, который менее гибок и не может кэшироваться, но имеет более высокий приоритет по сравнению с getConfig (позволяет переопределять) и позволяет также определять сервисные фабрики как замыкания:

class MyModule
{
    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'some-logger-name' => function ($sl) {
                    return new MyLogger($sl->get('some-db'));
                },
            ),
        );
    }
}

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

Концепция модулей и конфигураций заключается в том, что «выигрывает последний модуль», поэтому вы можете определить службу 'some-logger-name' либо в своем модуле, либо в любом модуле, загруженном до него.

Те же концепции применимы и к вашему соединению с БД.

Как видите, переход на сервисы уже дал вам определенную степень свободы.

Имейте в виду, что это не значит, что «Приложение» что-то определяет для вас: модули определяют ваши службы/конфигурации/события и т. д. Запущенное приложение представляет собой композицию всех этих вещей вместе.

person Ocramius    schedule 15.02.2013
comment
Я очень ценю рецензию (и быстро просмотрел предоставленную вами ссылку, сегодня я подробно изучу ее немного позже). Из-за трудностей, которые у меня были (частично отмеченных в сообщении с вопросом выше), я также начал читать исходный код ZF2, чтобы лучше понять рабочий процесс. Ваш ответ очень хорошо объяснен в сочетании с чтением исходного кода, я начинаю лучше понимать, как части сочетаются друг с другом. Я с нетерпением жду прочтения (ссылки), которую вы предоставили, так как на первый взгляд это похоже на то, что мне нужно для перехода с ZF1 на ZF2! - person Aaron Murray; 16.02.2013
comment
Учитывая, насколько новым является выпуск ZF2, а также количество изменений в процессе в стиле, управляемом службами/управляемом событиями, в доступной до сих пор информации существует огромное количество неясностей (по крайней мере, из того, с чем я столкнулся), я очень высоко ценим ссылку, которую вы предоставили, поскольку она, кажется, хорошо описывает переход. - person Aaron Murray; 16.02.2013
comment
@AaronMurray мастер ServiceManager и EventManager и все будет выглядеть намного проще. - person Ocramius; 16.02.2013
comment
Но в любом случае модули НЕ являются автономными. Их следует разрабатывать в изолированной среде и с как можно меньшим количеством зависимостей, но они обеспечивают функциональность перекрестных задач, которая влияет на все ваше приложение. - Еще по этой теме, по сути, это то, что я имел в виду под автономным - мало зависимостей или вообще никаких, поэтому любой может поместить модуль в свое собственное приложение и использовать его функциональность в своем собственном приложении. - person Aaron Murray; 16.02.2013
comment
Правильно, но не путайте модуль с контейнером. Модуль не является контейнером «вещей». Он обеспечивает общую функциональность/поведение. - person Ocramius; 16.02.2013
comment
В этом видео Мэтью Вейер О'Финни определяет модуль как: решение проблемы (единственное число) - например, контактная форма - позволяет конечному пользователю связаться, например, с владельцем сайта / блога. Эта контактная форма состоит из формы, которая отображается для конечного пользователя, кода при отправке для обработки (электронная почта, сохранение в базе данных и т. д.) отправки формы и ответа конечному пользователю о том, что форма была успешно отправлена. Это то, на чем я основываю свое «определение» модуля. Это очень простой пример, «проблемы» могут быть гораздо более глубокими. - person Aaron Murray; 16.02.2013
comment
Да, у модуля на самом деле нет определения, кроме пространства имен и самого класса Module. Это потому, что вы можете найти такие модули, как github.com/EvanDotPro/EdpSuperluminal (один класс) или github.com/doctrine/DoctrineORMModule и другие очень странные вещи :) Я предпочитаю определять многоуровневую функциональность, потому что это ближайшее соответствие тому, что (обычно) предоставляется - person Ocramius; 16.02.2013
comment
давайте продолжим это обсуждение в чате - person Aaron Murray; 16.02.2013
comment
Модуль может быть чем угодно, библиотекой, сценарием представления, целым автономным приложением, чем угодно. - person markus; 16.02.2013

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

Прикрепите прослушиватель событий log к вашему Module.php:

public function onBootstrap(MvcEvent $e)
{
    //setup some $logger

    $sharedManager = $e->getApplication()->getEventManager()->getSharedManager();

    $sharedManager->attach('*', 'log', function($e) use ($logger) {
        /** @var $e MvcEvent */
        $target   = get_class($e->getTarget());
        $message  = $e->getParam('message', 'No message provided');
        $priority = $e->getParam('priority', Logger::INFO);
        $message  = sprintf('%s: %s', $target, $message);
        $logger->log($priority, $message);
    });
}

Затем запустите его откуда в вашем приложении, например, из контроллера:

$this->getEventManager()->trigger('log', $this, array(
    'priority' => \Zend\Log\Logger::INFO, 
    'message' => 'just some info to be logged'
));
person markus    schedule 15.02.2013
comment
на самом деле речь идет не о том, чтобы регистратор был доступен во всем модуле, а в основном об объяснении того, как вы используете диспетчер событий для решения сквозных проблем. Это круто, но встроенный комментарий на самом деле является разыскиваемой частью: P - person Ocramius; 16.02.2013
comment
Я в некоторой степени понимаю, что говорит Маркус (через код), и согласен с @Ocramius в том, что это не столько код, о котором я спрашивал, сколько понимание того, как перейти от «ресурсов» Zend_Frameworks к тому, где теперь определить мои «ресурсы», чтобы они были доступны для всего приложения. Я начинаю рисовать более четкую картину с информацией в обоих ответах. Я подозреваю, что теперь я проведу большую часть выходных, чтобы хорошо понять ModuleManager, EventManager и ServiceManager, и я думаю, что я буду «конвертирован» - person Aaron Murray; 16.02.2013
comment
Должен отметить, что сложнее всего пройти мимо логики Events в среде без состояния, но до меня доходит :) - person Aaron Murray; 16.02.2013
comment
Именно целью этого ответа было добавить еще одну грань к ответу Окрамиуса, потому что в ZF2 вы найдете гораздо больше гибкости в том, как вы делаете что-то. Что делает это сложнее, но легче. - person markus; 16.02.2013
comment
Мне нравится концепция событий журнала. Таким образом, вы просто говорите, что вам нужно что-то зарегистрировать, оставляя решение за приложением, должно ли оно использовать событие или нет. Ради развязки/модульного тестирования я бы предпочел, чтобы классы моей модели не знали о структуре, но тогда каждый из классов должен быть осведомлен о Logger. Как-то застрял... Или дело в том, что мы логируем только с контроллеров? - person tishma; 03.02.2014
comment
@myself: Да, похоже, что модели должны общаться через результаты и исключения метода, а ведение журнала принадлежит приложению. - person tishma; 03.02.2014