Napisałem dokładniejszy i zaktualizowany post na blogu na ten temat: http://elnur.pro/symfony-without-bundles/
Nie, nie wszystko musi być w pakiecie. Możesz mieć taką strukturę:
src/Vendor/Model
- dla modeli,
src/Vendor/Controller
- dla kontrolerów,
src/Vendor/Service
- dla usług,
src/Vendor/Bundle
- dla pakietów, takich jak src/Vendor/Bundle/AppBundle
,
- itp.
W ten sposób umieściłbyś AppBundle
tylko te rzeczy, które są naprawdę specyficzne dla Symfony2. Jeśli zdecydujesz się później przełączyć na inny framework, pozbywamy się Bundle
przestrzeni nazw i zastępujemy ją wybranymi elementami frameworka.
Pamiętaj, że sugeruję tutaj kod specyficzny dla aplikacji . W przypadku pakietów wielokrotnego użytku nadal sugeruję stosowanie najlepszych praktyk .
Utrzymywanie podmiotów poza pakietami
Aby jednostki pozostały src/Vendor/Model
poza jakimkolwiek pakietem, zmieniłem doctrine
sekcję w config.yml
z
doctrine:
# ...
orm:
# ...
auto_mapping: true
do
doctrine:
# ...
orm:
# ...
mappings:
model:
type: annotation
dir: %kernel.root_dir%/../src/Vendor/Model
prefix: Vendor\Model
alias: Model
is_bundle: false
Nazwy podmiotów - aby uzyskać dostęp z repozytoriów Doctrine - zaczynają się Model
na przykład w tym przypadku Model:User
.
Za pomocą przestrzeni nazw można grupować powiązane jednostki, na przykład src/Vendor/User/Group.php
. W tym przypadku nazwa jednostki to Model:User\Group
.
Trzymanie kontrolerów poza pakietami
Najpierw musisz powiedzieć JMSDiExtraBundle, aby przeskanował src
folder w poszukiwaniu usług, dodając to do config.yml
:
jms_di_extra:
locations:
directories: %kernel.root_dir%/../src
Następnie definiujesz kontrolery jako usługi i umieszczasz je w Controller
przestrzeni nazw:
<?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
];
}
}
Zauważ, że używam mojego ElnurAbstractControllerBundle, aby uprościć definiowanie kontrolerów jako usług.
Ostatnią rzeczą jest, aby powiedzieć Symfony, aby szukała szablonów bez pakietów. Robię to, zastępując usługę odgadywania szablonów, ale ponieważ podejście jest inne w Symfony 2.0 i 2.1, udostępniam wersje dla obu z nich.
Przesłonięcie zgadywacza szablonów Symfony 2.1+
Stworzyłem pakiet, który robi to za Ciebie.
Przesłanianie nasłuchiwania szablonu Symfony 2.0
Najpierw zdefiniuj klasę:
<?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;
}
}
}
A następnie powiedz Symfony, aby go użył, dodając to do config.yml
:
parameters:
jms_di_extra.template_listener.class: Vendor\Listener\TemplateListener
Korzystanie z szablonów bez pakietów
Teraz możesz używać szablonów z pakietów. Przechowuj je w app/Resources/views
folderze. Na przykład szablony tych dwóch akcji z powyższego przykładowego kontrolera znajdują się w:
app/Resources/views/User/add.html.twig
app/Resources/views/User/profile.html.twig
Odnosząc się do szablonu, po prostu pomiń część pakietu:
{% include ':Controller:view.html.twig' %}