Мне известны такие вопросы, как , где люди склонны обсуждать общую концепцию пакета Symfony 2.
Дело в том, что в конкретном приложении, например, в твиттер-подобном приложении, все должно быть внутри общего набора, например, официальный docs говорят?
Причина, по которой я спрашиваю об этом, заключается в том, что, когда мы разрабатываем приложения, в общем, мы не хотим сильно привязывать наш код к какому-то полному фрейму клея.
Если я разработаю приложение на базе Symfony 2, и в какой-то момент я решаю, что Symfony 2 на самом деле не лучший выбор для продолжения развития, это будет проблемой для меня?
Итак, общий вопрос: почему все, что является связкой, хорошо?
РЕДАКТИРОВАТЬ # 1
Почти через год, после того как я задал этот вопрос, я написал статью , чтобы поделиться своими знаниями по этой теме.
Я написал более подробное и обновленное сообщение в блоге по этой теме: http://elnur.pro/symfony-without-bundles/
Нет, не все должно быть в связке. У вас может быть такая структура:
src/Vendor/Model
- для моделей,src/Vendor/Controller
- для контроллеров,src/Vendor/Service
- для служб,src/Vendor/Bundle
- для пучков, например src/Vendor/Bundle/AppBundle
,Таким образом, вы добавили бы в AppBundle
только тот материал, который действительно специфичен для Symfony2. Если вы решите перейти на другую структуру позже, вы избавитесь от пространства имен Bundle
и замените ее на выбранные фреймворки.
Обратите внимание, что здесь я предлагаю использовать приложение. Для многоразовых пакетов я по-прежнему предлагаю использовать лучшие методы.
Чтобы сохранить объекты в src/Vendor/Model
вне любого пакета, я изменил раздел doctrine
в config.yml
из
doctrine:
# ...
orm:
# ...
auto_mapping: true
к
doctrine:
# ...
orm:
# ...
mappings:
model:
type: annotation
dir: %kernel.root_dir%/../src/Vendor/Model
prefix: Vendor\Model
alias: Model
is_bundle: false
Имена объектов - для доступа из репозиториев Doctrine - начните с Model
в этом случае, например, Model:User
.
Вы можете использовать пространства имен для группирования связанных объектов вместе, например, src/Vendor/User/Group.php
. В этом случае имя объекта Model:User\Group
.
Сначала вам нужно сообщить JMSDiExtraBundle сканирование папки src
для служб, добавив ее в config.yml
:
jms_di_extra:
locations:
directories: %kernel.root_dir%/../src
Затем вы определяете контроллеры как службы и помещаете их в пространство имен Controller
:
<?php
namespace Vendor\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\InjectParams;
use JMS\SecurityExtraBundle\Annotation\Secure;
use Elnur\AbstractControllerBundle\AbstractController;
use Vendor\Service\UserService;
use Vendor\Model\User;
/**
* @Service("user_controller", parent="elnur.controller.abstract")
* @Route(service="user_controller")
*/
class UserController extends AbstractController
{
/**
* @var UserService
*/
private $userService;
/**
* @InjectParams
*
* @param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
/**
* @Route("/user/add", name="user.add")
* @Template
* @Secure("ROLE_ADMIN")
*
* @param Request $request
* @return array
*/
public function addAction(Request $request)
{
$user = new User;
$form = $this->formFactory->create('user', $user);
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$this->userService->save($user);
$request->getSession()->getFlashBag()->add('success', 'user.add.success');
return new RedirectResponse($this->router->generate('user.list'));
}
}
return ['form' => $form->createView()];
}
/**
* @Route("/user/profile", name="user.profile")
* @Template
* @Secure("ROLE_USER")
*
* @param Request $request
* @return array
*/
public function profileAction(Request $request)
{
$user = $this->getCurrentUser();
$form = $this->formFactory->create('user_profile', $user);
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$this->userService->save($user);
$request->getSession()->getFlashBag()->add('success', 'user.profile.edit.success');
return new RedirectResponse($this->router->generate('user.view', [
'username' => $user->getUsername()
]));
}
}
return [
'form' => $form->createView(),
'user' => $user
];
}
}
Обратите внимание, что я использую ElnurAbstractControllerBundle, чтобы упростить определение контроллеров как служб.
Последнее, что нужно сказать, это заставить Symfony искать шаблоны без пакетов. Я делаю это, переопределяя службу угадывания шаблона, но поскольку подход отличается от Symfony 2.0 и 2.1, я предоставляю версии для них обоих.
Я создал bundle, который делает это для вас.
Сначала определите класс:
<?php
namespace Vendor\Listener;
use InvalidArgumentException;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener as FrameworkExtraTemplateListener;
use JMS\DiExtraBundle\Annotation\Service;
class TemplateListener extends FrameworkExtraTemplateListener
{
/**
* @param array $controller
* @param Request $request
* @param string $engine
* @throws InvalidArgumentException
* @return TemplateReference
*/
public function guessTemplateName($controller, Request $request, $engine = 'twig')
{
if (!preg_match('/Controller\\\(.+)Controller$/', get_class($controller[0]), $matchController)) {
throw new InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it must be in a "Controller" sub-namespace and the class name must end with "Controller")', get_class($controller[0])));
}
if (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) {
throw new InvalidArgumentException(sprintf('The "%s" method does not look like an action method (it does not end with Action)', $controller[1]));
}
$bundle = $this->getBundleForClass(get_class($controller[0]));
return new TemplateReference(
$bundle ? $bundle->getName() : null,
$matchController[1],
$matchAction[1],
$request->getRequestFormat(),
$engine
);
}
/**
* @param string $class
* @return Bundle
*/
protected function getBundleForClass($class)
{
try {
return parent::getBundleForClass($class);
} catch (InvalidArgumentException $e) {
return null;
}
}
}
И затем скажите Symfony, чтобы использовать его, добавив это в config.yml
:
parameters:
jms_di_extra.template_listener.class: Vendor\Listener\TemplateListener
Теперь вы можете использовать шаблоны из комплектов. Храните их в папке app/Resources/views
. Например, шаблоны для этих двух действий из приведенного выше примера контроллера расположены в:
app/Resources/views/User/add.html.twig
app/Resources/views/User/profile.html.twig
При обращении к шаблону просто опустите часть пакета:
{% include ':Controller:view.html.twig' %}
Конечно, вы можете отделить свое приложение. Просто создайте его как библиотеку и интегрируйте ее в symfony vendor/
-folder (либо с помощью deps
, либо composer.json
, в зависимости от того, используете ли вы Symfony2.0 или Symfony2.1). Однако вам нужен хотя бы один пакет, который действует как "интерфейс" вашей библиотеки, где Symfony2 находит контроллер (и такой).
symfony-2.0
я предполагаю, что вы используете текущую версию 2.0. В этом случае создайте git-репозиторий там, где вам нравится, и поместите в него все, что вы хотите разрабатывать независимо от Symfony. В вашем symfony-проекте обновите файл deps
как упомянуто здесь: symfony.com/doc/current/cookbook/workflow/… Затем просто создайте один (или несколько) набор (-ов) php app/console generate:bundle
( php app/console generate:bundle
) для специфичные для Symfony вещи.
Обычное распределение symfony может работать без дополнительного (прикладного) пакета, в зависимости от того, сколько функций вы хотите использовать из полной структуры стека.
Например, ваши контроллеры могут быть любыми вызываемыми, которые могут быть помещены в любом месте вашей структуры проекта, как только они будут автоматически загружены.
В файле определения маршрутизации вы можете использовать:
test:
pattern: /test
defaults: { _controller: Controller\Test::test }
Это может быть любой простой старый php-объект, связанный только с каркасом тем фактом, что он должен вернуть объект Symfony\Component\HttpFoundation\Response
.
Ваши шаблоны ветки (или другие) можно поместить как app/Resources/views/template.html.twig
и могут быть отображены с использованием логического имени ::template.html.twig
.
Все сервисы DI могут быть определены в app/config/config.yml(или импортированы из app/config/services.yml
, например, и все классы обслуживания могут быть любыми простыми старыми объектами php тоже не привязаны к структуре вообще.
Все это предоставляется по умолчанию с помощью полнофункциональной структуры symfony.
Если у вас возникнут проблемы, когда вы захотите использовать файлы перевода (например, xliff), потому что они обнаруживаются через пакеты только.
Распространение symfony-light направлено на решение этих проблем путем обнаружения всего, что обычно обнаруживалось бы только через пакеты.
С тех пор прошло уже 5 лет, вот еще несколько статей о пакетах Symfony.
TL;DR:
Вам нужно несколько пакетов в приложении напрямую? Скорее всего не. Вам лучше писать AppBundle, чтобы предотвратить спагетти зависимостей. Вы можете просто следовать рекомендациям и это будет хорошо работайте.
TL;DR:
Создайте только один пакет под названием AppBundle для вашей логики приложения. Один AppBundle - но, пожалуйста, не размещайте свою логику приложения!
Вы можете использовать KnpRadBundle, который пытается упростить структуру проекта.
Другим подходом является использование src/Company/Bundle/FrontendBundle
, например, для пакетов и src/Company/Stuff/Class.php
для классов, которые являются независимыми от symfony и которые могут быть повторно использованы вне рамки
Структура Symfony очень хороша для быстрого запуска доказательства концепции, и весь код может войти в приложение по умолчанию по умолчанию в src/
В этом комплекте вы можете структурировать свой код, как хотите.
После того, как вы хотите использовать другие технологии для разработки своего POC, вы можете легко перевести это, потому что вы не структурируете весь свой код в концепции пакета.
Для всей концепции вы не экстремизируете это. Пакет хорош, но все связывает, и каждый день нехорошо.
Возможно, вы можете использовать Silex (Symfony micro framework) для разработки вашего Proof of Concept для снижения влияния сторонних поставщиков.