Czy wszystko powinno być naprawdę pakietem w Symfony 2.x?


205

Jestem świadomy kwestii jak to , gdzie ludzie mają tendencję, aby omówić ogólną koncepcję Symfony 2 wiązki.

Chodzi o to, że w konkretnej aplikacji, takiej jak na przykład aplikacja podobna do Twittera, czy wszystko naprawdę powinno znajdować się w pakiecie ogólnym, jak mówią oficjalne dokumenty ?

Powód, dla którego o to pytam, jest taki, że generalnie, gdy tworzymy aplikacje, nie chcemy zbytnio wiązać naszego kodu z jakimś szkieletem kleju z pełnym stosem.

Jeśli opracuję aplikację opartą na Symfony 2 i w pewnym momencie zdecyduję, że Symfony 2 nie jest najlepszym wyborem, aby kontynuować rozwój , czy będzie to dla mnie problem?

Ogólne pytanie brzmi: dlaczego wszystko jest w pakiecie dobrą rzeczą?

EDYCJA 1

Prawie rok odkąd zadałem to pytanie, napisałem artykuł, aby podzielić się swoją wiedzą na ten temat.


1
To tylko komentarz, a nie odpowiedź. Osobiście uważam, że powinniśmy starannie wybrać ramy przed rozpoczęciem projektu. Każdy framework ma swój własny sposób robienia rzeczy, więc zapewni narzędzia do wspierania tego w najlepszy sposób. Jeśli podoba nam się to, podążamy. Tam są inne możliwości. Nie chcemy używać noża do cięcia drewna zamiast piły. Ale to bardzo interesujące pytanie, które postawiłeś :)
Anh Nguyen,

Odpowiedzi:


219

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ś AppBundletylko te rzeczy, które są naprawdę specyficzne dla Symfony2. Jeśli zdecydujesz się później przełączyć na inny framework, pozbywamy się Bundleprzestrzeni 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/Modelpoza jakimkolwiek pakietem, zmieniłem doctrinesekcję w config.ymlz

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ę Modelna 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ł srcfolder 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 Controllerprzestrzeni 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/viewsfolderze. 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' %}

2
To naprawdę interesujące podejście. Dzięki temu mogę również opracowywać prawdziwe pakiety zawierające określony zestaw funkcji, z których może korzystać społeczność, bez trudu łącząc moją aplikację z samą strukturą.
Daniel Ribeiro,

57
Aby kod, który udostępniasz społeczności, nie był również powiązany z Symfony2, możesz umieścić ogólne rzeczy w bibliotece, a następnie utworzyć pakiet, który integruje tę bibliotekę z Symfony2.
Elnur Abdurrakhimov

9
To ciekawy pomysł, o ile nie polegasz na żadnym poleceniu do generowania kodu. generate:doctrine:crudna przykład oczekuje, że jednostka (= model w przypadku elnur) będzie w pakiecie, aby działać.
geca

2
Czy przy takim podejściu można odzyskać funkcjonalność interfejsu aplikacji / konsoli CLI? Podoba mi się pomysł trzymania moich modeli w miejscu poza jakimkolwiek pakietem, ale chciałbym zachować dostęp do funkcjonalności CLI.
Andy Baird

3
Powinno to być umieszczone w pakiecie :)
d0001

20

Oczywiście możesz oddzielić swoją aplikację. Po prostu opracuj go jako bibliotekę i zintegruj z vendor/folderem symfony (używając depsalbo lub composer.json, zależnie od tego, czy używasz Symfony2.0 lub Symfony2.1). Potrzebujesz jednak co najmniej jednego pakietu, który działa jak „frontend” twojej biblioteki, w którym Symfony2 znajduje kontroler (i tak dalej).


2
Ze względu na znacznik symfony-2.0założę, że używasz bieżącej wersji 2.0. W takim przypadku stwórz repozytorium git, gdziekolwiek chcesz i umieść w nim wszystko, co chcesz rozwijać niezależnie od symfony. W swoim projekcie symfony zaktualizuj swój depsplik, jak wspomniano tutaj symfony.com/doc/current/cookbook/workflow/... Następnie po prostu utwórz jeden (lub więcej) pakiet aplikacji ( php app/console generate:bundle) dla rzeczy specyficznych dla symfony.
KingCrunch,

11

Zwykła dystrybucja symfony może działać bez dodatkowego pakietu (aplikacji), w zależności od tego, ile funkcjonalności chcesz użyć z frameworku pełnego stosu.

Na przykład kontrolery mogą być dowolne na żądanie, które można umieścić w dowolnym miejscu w strukturze projektu, gdy tylko zostaną automatycznie załadowane.

W pliku definicji routingu możesz użyć:

test:
    pattern:   /test
    defaults:  { _controller: Controller\Test::test }

Może to być dowolny zwykły stary obiekt php, powiązany tylko z ramą przez fakt, że musi zwrócić a Symfony\Component\HttpFoundation\Response obiekt.

Twoje szablony gałązek (lub inne) można ustawić w podobny sposób app/Resources/views/template.html.twigi można je renderować za pomocą::template.html.twig logicznej nazwy.

Wszystkie usługi DI można zdefiniować w app / config / config.yml (lub zaimportować z app/config/services.yml na przykład , a wszystkie klasy usług mogą być również zwykłymi starymi obiektami php. W ogóle nie związanymi z frameworkiem.

Wszystko to jest domyślnie dostarczane przez framework pełnego stosu symfony.

Problemy będziesz miał wtedy, gdy będziesz chciał użyć plików do tłumaczenia (takich jak xliff), ponieważ są one wykrywane tylko za pomocą pakietów .

Rozkład światła symfony ma na celu rozwiązanie tego rodzaju problemów poprzez odkrycie wszystkiego, co zwykle można odkryć tylko za pomocą pakietów.


5

Możesz użyć KnpRadBundle , który próbuje uprościć strukturę projektu.

Innym podejściem jest użycie src/Company/Bundle/FrontendBundlena przykład dla pakietów i src/Company/Stuff/Class.phpklas, które są niezależne od symfony i które mogą być ponownie użyte poza frameworkiem


Ale wtedy podłączałbym aplikację do KnpRadBundle ... Czy nie ma łatwiejszego podejścia w tej sprawie?
Daniel Ribeiro,

1
Części zależne od symfony (kontrolery, modele, szablony itp.) Zawsze będą sprzężone z symfony, ponieważ używasz go (rozszerzanie klas, używanie pomocników itp.). Klasy, które działają samodzielnie, będą znajdować się w przestrzeni nazw firmy i można je ładować przy użyciu kontenera zależności. Klasy te mogą być niezależne od ram.
miguel_ibero

1
Chodzi o to, że koncepcja Bundleidzie bezpośrednio na publiczne udostępnianie. Kiedy piszę jakąś aplikację, nie chcę udostępniać mojego kodu, z wyjątkiem tych części, które celowo zbudowałem jako moduły sterowane przez społeczność. Czy się mylę?
Daniel Ribeiro,

Nie musisz udostępniać pakietów. Pomyśl o pakiecie jak o grupie klas z pewną konfiguracją. W każdym projekcie możesz mieć różne pakiety.
miguel_ibero

Powinieneś przeczytać książkę o symfony
miguel_ibero

5

Ponieważ minęło już 5 lat, oto kilka artykułów o pakietach Symfony.

  1. Co to są pakiety w Symfony? autor: Iltar van der Berg.

TLDR:

Czy potrzebujesz bezpośrednio wielu pakietów w swojej aplikacji? Najprawdopodobniej nie. Lepiej napisz pakiet AppBundle, aby uniknąć spaghetti zależności. Możesz po prostu postępować zgodnie z najlepszymi praktykami, a wszystko będzie dobrze.

  1. Symfony: Jak dołączyć Toni Uebernickel.

TLDR:

Utwórz tylko jeden pakiet o nazwie AppBundle dla logiki aplikacji. Jeden pakiet aplikacji - ale nie umieszczaj tam logiki aplikacji!


-2

Środowisko Symfony jest bardzo dobre do szybkiego uruchomienia testu koncepcji, a cały kod można wprowadzić w domyślnej aplikacji pakietu w src /

W tym pakiecie możesz uporządkować swój kod według swoich potrzeb.

Po tym, jeśli chcesz użyć innej technologii do opracowania swojego POC, możesz łatwo to przetłumaczyć, ponieważ nie tworzysz całego kodu w koncepcji pakietu.

Mimo całej koncepcji nie ekstremistycznie tego. Pakiet jest dobry, ale pakuj wszystko, a codzienność nie jest dobra.

Być może możesz użyć Silex (Symfony micro framework) do opracowania Proof of Concept w celu zmniejszenia wpływu pakietu innych firm.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.