Tutto dovrebbe essere davvero un pacchetto in Symfony 2.x?


205

Sono a conoscenza di domande come questa , in cui le persone tendono a discutere del concetto generale di bundle di Symfony 2.

Il fatto è che in un'applicazione specifica, come ad esempio un'applicazione simile a Twitter, tutto dovrebbe essere davvero all'interno di un pacchetto generico, come dicono i documenti ufficiali ?

Il motivo per cui lo sto chiedendo è perché quando sviluppiamo applicazioni, in generale, non vogliamo abbinare fortemente il nostro codice a un framework di colla full-stack.

Se sviluppo un'applicazione basata su Symfony 2 e, a un certo punto, decido che Symfony 2 non è davvero la scelta migliore per continuare lo sviluppo , sarà un problema per me?

Quindi la domanda generale è: perché tutto è un bundle una cosa buona?

EDIT # 1

Quasi un anno da quando ho posto questa domanda ho scritto un articolo per condividere le mie conoscenze su questo argomento.


1
Questo è solo un commento, non una risposta. Personalmente penso che dovremmo scegliere attentamente il framework prima di iniziare il progetto. Ogni framework ha il suo modo di fare le cose, quindi fornirà gli strumenti per supportare in quel modo il meglio. Se ci piace in questo modo, seguiamo. Ci sono altre scelte là fuori. Non vogliamo usare un coltello per tagliare il legno anziché una sega. Ma è una domanda molto interessante che hai posto :)
Anh Nguyen,

Risposte:


219

Ho scritto un post sul blog più approfondito e aggiornato su questo argomento: http://elnur.pro/symfony-without-bundles/


No, non tutto deve essere in un fascio. Potresti avere una struttura come questa:

  • src/Vendor/Model - per i modelli,
  • src/Vendor/Controller - per i controller,
  • src/Vendor/Service - per servizi,
  • src/Vendor/Bundle- per fasci, come src/Vendor/Bundle/AppBundle,
  • eccetera.

In questo modo, inseriresti le AppBundleuniche cose specifiche di Symfony2. Se in seguito decidessi di passare a un altro framework, elimineresti lo Bundlespazio dei nomi e lo sostituiresti con le cose del framework scelto.

Si noti che ciò che sto suggerendo qui è per il codice specifico dell'app . Per i pacchetti riutilizzabili, suggerisco comunque di utilizzare le migliori pratiche .

Tenere le entità fuori dai fasci

Per mantenere le entità src/Vendor/Modelall'esterno di qualsiasi pacchetto, ho modificato la doctrinesezione config.ymlda

doctrine:
    # ...
    orm:
        # ...
        auto_mapping: true

per

doctrine:
    # ...
    orm:
        # ...
        mappings:
            model:
                type: annotation
                dir: %kernel.root_dir%/../src/Vendor/Model
                prefix: Vendor\Model
                alias: Model
                is_bundle: false

I nomi delle entità - per accedere dai repository di Doctrine - iniziano Modelin questo caso, ad esempio Model:User.

È possibile utilizzare gli spazi dei nomi secondari per raggruppare entità correlate, ad esempio src/Vendor/User/Group.php. In questo caso, il nome dell'entità è Model:User\Group.

Mantenere i controller fuori dai fasci

Innanzitutto, devi dire a JMSDiExtraBundle di scansionare la srccartella alla ricerca di servizi aggiungendo questo a config.yml:

jms_di_extra:
    locations:
        directories: %kernel.root_dir%/../src

Quindi definisci i controller come servizi e li inserisci nello Controllerspazio dei nomi:

<?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
        ];
    }
}

Nota che sto usando il mio ElnurAbstractControllerBundle per semplificare la definizione di controller come servizi.

L'ultima cosa rimasta è dire a Symfony di cercare template senza bundle. Lo faccio sovrascrivendo il servizio di indovinare i template, ma poiché l'approccio è diverso tra Symfony 2.0 e 2.1, sto fornendo versioni per entrambi.

Sostituire l'ipotesi del template di Symfony 2.1+

Ho creato un pacchetto che lo fa per te.

Sostituzione del listener di template di Symfony 2.0

Innanzitutto, definisci la classe:

<?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;
        }
    }
}

E quindi dire a Symfony di usarlo aggiungendo questo a config.yml:

parameters:
    jms_di_extra.template_listener.class: Vendor\Listener\TemplateListener

Utilizzo di modelli senza pacchetti

Ora puoi utilizzare i modelli in bundle. Tienili nella app/Resources/viewscartella. Ad esempio, i modelli per queste due azioni dal controller di esempio sopra si trovano in:

  • app/Resources/views/User/add.html.twig
  • app/Resources/views/User/profile.html.twig

Quando si fa riferimento a un modello, è sufficiente omettere la parte del bundle:

{% include ':Controller:view.html.twig' %}

2
Questo è in realtà un approccio davvero interessante. Con ciò, posso anche sviluppare bundle reali che contengono un set specifico di funzionalità che la community può utilizzare, senza difficilmente accoppiare la mia applicazione al framework stesso.
Daniel Ribeiro,

57
Per rendere il codice che condividi con la community non accoppiato anche con Symfony2, puoi mettere le cose generali in una libreria e quindi creare un bundle che integri quella libreria con Symfony2.
Elnur Abdurrakhimov,

9
Questa è un'idea interessante fintanto che non fai affidamento su nessuno dei comandi di generazione del codice. generate:doctrine:crudper esempio si aspetta che l'entità (= modello nel caso di elnur) sia all'interno di un bundle per funzionare.
geca,

2
Con questo approccio c'è un modo per riguadagnare la funzionalità dell'interfaccia della console / dell'app CLI? Adoro l'idea di mantenere i miei modelli in una posizione al di fuori di qualsiasi pacchetto, ma mi piacerebbe mantenere l'accesso alla funzionalità CLI.
Andy Baird,

3
Questo dovrebbe essere messo in un fascio :)
d0001

20

Ovviamente puoi disaccoppiare la tua applicazione. Sviluppalo come libreria e integralo nella vendor/cartella symfony (usando depso oppure composer.json, a seconda che usi Symfony2.0 o Symfony2.1). Tuttavia, è necessario almeno un bundle, che funge da "frontend" della libreria, in cui Symfony2 trova il controller (e simili).


2
A causa del tag symfony-2.0, suppongo che tu usi l'attuale versione 2.0. In questo caso crea un repository git dove preferisci e metti tutto al suo interno, cosa vuoi sviluppare indipendentemente da symfony. Nel tuo progetto symfony aggiorna il tuo depsfile come menzionato qui symfony.com/doc/current/cookbook/workflow/… Quindi crea uno (o più) bundle (s) di applicazione ( php app/console generate:bundle) per le cose specifiche di symfony.
KingCrunch

11

Una normale distribuzione di symfony può funzionare senza alcun pacchetto aggiuntivo (applicazione), a seconda di quante funzionalità si desidera utilizzare dal framework dello stack completo.

Ad esempio, i controller possono essere richiamabili ovunque nella struttura del progetto, non appena vengono caricati automaticamente.

In un file di definizione del routing, è possibile utilizzare:

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

Può essere qualsiasi vecchio oggetto php semplice, legato solo al framework dal fatto che deve restituire un Symfony\Component\HttpFoundation\Responseoggetto.

I tuoi modelli di ramoscelli (o altri) possono essere messi come app/Resources/views/template.html.twige possono essere resi usando il ::template.html.twignome logico.

Tutti i servizi DI possono essere definiti in app / config / config.yml (o importati app/config/services.ymlad esempio, e tutte le classi di servizio possono essere anche qualsiasi vecchio oggetto php semplice. Non legato affatto al framework.

Tutto questo è fornito di default dal framework full stack di symfony.

Il punto in cui si verificano problemi è quando si desidera utilizzare i file di traduzione (come xliff), perché vengono scoperti solo tramite pacchetti .

La distribuzione di symfony-light mira a risolvere questo tipo di problemi scoprendo tutto ciò che di solito sarebbe scoperto solo attraverso i bundle.


5

È possibile utilizzare KnpRadBundle , che tenta di semplificare la struttura del progetto.

Un altro approccio consiste nell'utilizzare src/Company/Bundle/FrontendBundlead esempio i bundle e src/Company/Stuff/Class.phple classi indipendenti da symfony e che potrebbero essere riutilizzati al di fuori del framework


Ma poi assocerei l'applicazione a KnpRadBundle ... Non esiste un approccio più semplice in materia?
Daniel Ribeiro,

1
Le parti che dipendono da symfony (controller, modelli, modelli, ecc ...) saranno sempre accoppiate a symfony, poiché la stai usando (estendere le classi, usare gli helper, ecc ...). Le classi che funzionano da sole saranno nello spazio dei nomi dell'azienda e puoi caricarle utilizzando il contenitore delle dipendenze. Queste classi possono essere indipendenti dal framework.
miguel_ibero,

1
Il fatto è che il concetto di Bundleva direttamente sulla condivisione pubblica. Quando scrivo qualche applicazione, non voglio condividere il mio codice, ad eccezione di quelle parti che ho costruito intenzionalmente come moduli guidati dalla comunità. Ho sbagliato?
Daniel Ribeiro,

Non è necessario condividere i pacchetti. Pensa a un gruppo come a un gruppo di classi con una configurazione. In ogni progetto puoi avere fasci diversi.
miguel_ibero,

Dovresti leggere il libro di symfony
miguel_ibero,

5

Dato che sono già passati 5 anni, ecco alcuni altri articoli sui bundle di Symfony.

  1. Cosa sono i bundle in Symfony? di Iltar van der Berg.

TLDR:

Hai bisogno di più bundle direttamente nella tua applicazione? Molto probabilmente no. Stai meglio scrivendo un AppBundle per evitare uno spaghetti di dipendenze. Puoi semplicemente seguire le migliori pratiche e funzionerà bene.

  1. Symfony: How to Bundle di Toni Uebernickel.

TLDR:

Crea un solo bundle chiamato AppBundle per la logica dell'applicazione. Un AppBundle - ma per favore non inserire la logica dell'applicazione!


-2

Il framework symfony è ottimo per avviare rapidamente una prova di concetto e tutto il codice può entrare nell'applicazione bundle predefinita in src /

In questo pacchetto è possibile strutturare il codice come desiderato.

Dopo, se vuoi usare altre tecnologie per sviluppare il tuo POC, puoi facilmente tradurlo perché non strutturi tutto il codice nella concezione del bundle.

Per tutto il concetto non hai estremizzato questo. Il bundle è buono ma raggruppa tutto e ogni giorno non è buono.

Forse puoi usare un Silex (micro framework Symfony) per sviluppare la tua Proof of Concept per ridurre l'impatto di bundle di terze parti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.