208 votes

Tout doit-il vraiment être un bundle dans Symfony 2.x ?

Je suis conscient de questions comme este où les gens ont tendance à discuter du concept général de bundle dans Symfony 2.

Le problème est que, dans une application spécifique, comme par exemple une application de type Twitter, tout doit-il vraiment se trouver à l'intérieur d'un paquet générique, comme le paquet documents officiels dire ?

Si je pose cette question, c'est parce que lorsque nous développons des applications, en général, nous ne voulons pas coupler fortement notre code à un framework de collage complet.

Si je développe une application basée sur Symfony 2 et qu'à un moment donné, je décide que Symfony 2 n'est pas vraiment le meilleur choix pour poursuivre le développement Est-ce que ça sera un problème pour moi ?

La question générale est donc la suivante : pourquoi le fait que tout soit un paquet est-il une bonne chose ?

EDIT#1

Il y a presque un an maintenant que j'ai posé cette question, j'ai rédigé une article pour partager mes connaissances sur ce sujet.

1 votes

C'est juste un commentaire, pas une réponse. Je pense personnellement que nous devrions choisir le framework avec soin avant de commencer le projet. Chaque framework a sa propre façon de faire les choses, il fournira donc des outils pour soutenir cette façon le mieux. Si nous aimons cette façon de faire, nous la suivons. Il y a d'autres choix possibles. Nous ne voulons pas utiliser un couteau pour couper le bois au lieu d'une scie. Mais c'est une question très intéressante que vous avez posée :)

221voto

Elnur Abdurrakhimov Points 23540

J'ai rédigé un article de blog plus complet et actualisé sur ce sujet : http://elnur.pro/symfony-without-bundles/


Non, tout ne doit pas être dans un paquet. Vous pourriez avoir une structure comme celle-ci :

  • src/Vendor/Model - pour les modèles,
  • src/Vendor/Controller - pour les contrôleurs,
  • src/Vendor/Service - pour les services,
  • src/Vendor/Bundle - pour les paquets, comme src/Vendor/Bundle/AppBundle ,
  • etc.

De cette façon, vous mettez dans le AppBundle seulement les choses qui sont vraiment spécifiques à Symfony2. Si vous décidez de passer à un autre framework plus tard, vous vous débarrasserez de la section Bundle et le remplacer par le contenu du framework choisi.

_Veuillez noter que ce que je suggère ici est pour application code spécifique. Pour les paquets réutilisables, je suggère toujours l'utilisation de les meilleures pratiques ._

Garder les entités hors des liasses

Pour garder les entités dans src/Vendor/Model en dehors de tout regroupement, j'ai modifié le fichier doctrine section dans config.yml de

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

Les noms d'entités - à accéder à partir des référentiels Doctrine - commencent avec Model dans ce cas, par exemple, Model:User .

Vous pouvez utiliser des sous-espaces de nommage pour regrouper des entités apparentées, par exemple, src/Vendor/User/Group.php . Dans ce cas, le nom de l'entité est Model:User\Group .

Garder les contrôleurs hors des paquets

D'abord, vous devez dire JMSDiExtraBundle pour scanner le src pour les services en ajoutant ceci à config.yml :

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

Alors vous définir les contrôleurs comme des services et les mettre sous le Controller espace de noms :

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

Notez que j'utilise mon ElnurAbstractControllerBundle pour simplifier la définition des contrôleurs en tant que services.

La dernière chose qui reste à faire est d'indiquer à Symfony de rechercher les modèles sans bundle. Je fais cela en surchargeant le service template guesser, mais puisque l'approche est différente entre Symfony 2.0 et 2.1, je fournis des versions pour les deux.

Remplacer le devineur de modèle de Symfony 2.1+.

J'ai créé un paquet qui fait ça pour vous.

Remplacement de l'écouteur de modèle de Symfony 2.0

Tout d'abord, définissez 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;
        }
    }
}

Et puis dites à Symfony de l'utiliser en ajoutant ceci à config.yml :

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

Utilisation de modèles sans bundles

Maintenant, vous pouvez utiliser des modèles à partir de liasses. Gardez-les sous le app/Resources/views dossier. Par exemple, les modèles pour ces deux actions de l'exemple de contrôleur ci-dessus sont situés dans :

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

Lorsque vous faites référence à un modèle, omettez simplement la partie "bundle" :

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

2 votes

C'est en fait une approche très intéressante. Avec cela, je peux aussi développer de véritables bundles qui contiennent un ensemble spécifique de fonctionnalités que la communauté peut utiliser, sans pratiquement coupler mon application au framework lui-même.

57 votes

Pour faire en sorte que le code que vous partagez avec la communauté ne soit pas couplé à Symfony2, vous pourriez mettre les trucs généraux dans une bibliothèque et ensuite créer un bundle qui intègre cette bibliothèque avec Symfony2.

9 votes

C'est une idée intéressante tant que vous ne comptez pas sur l'une des commandes de génération de code. generate:doctrine:crud par exemple, s'attend à ce que l'entité (= le modèle dans le cas d'elnur) soit à l'intérieur d'un paquet pour pouvoir fonctionner.

21voto

KingCrunch Points 45168

Bien sûr, vous pouvez découpler votre application. Développez-la simplement en tant que bibliothèque et intégrez-la dans symfony. vendor/ -(soit en utilisant l'option deps o composer.json (selon que vous utilisez Symfony2.0 ou Symfony2.1). Cependant, vous avez besoin d'au moins un bundle, qui agit comme le "frontend" de votre bibliothèque, où Symfony2 trouve le contrôleur (et autres).

2 votes

En raison de l'étiquette symfony-2.0 Je suppose que vous utilisez la version 2.0 actuelle. Dans ce cas, créez un dépôt git où vous voulez et mettez-y tout ce que vous voulez développer indépendamment de symfony. Dans votre projet symfony, mettez à jour votre fichier deps -comme mentionné ici symfony.com/doc/current/cookbook/workflow/ Il suffit ensuite de créer un (ou plusieurs) paquet(s) d'applications ( php app/console generate:bundle ) pour les éléments spécifiques à la symfonie.

11voto

Florian Points 5388

Une distribution symfony habituelle peut fonctionner sans bundle (d'application) supplémentaire, en fonction de la quantité de fonctionnalités que vous souhaitez utiliser à partir du framework full stack.

Par exemple, vos contrôleurs peuvent être n'importe quelle callable qui peut être placée n'importe où dans la structure de votre projet, dès lors qu'ils sont autoloadés.

Dans un fichier de définition de gamme, vous pouvez utiliser :

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

Il peut s'agir de n'importe quel objet php, qui n'est lié à la structure que par le fait qu'il doit retourner un objet Symfony\Component\HttpFoundation\Response objet.

Vos modèles de twig (ou autres) peuvent être placés comme suit app/Resources/views/template.html.twig et peut être rendu à l'aide de la fonction ::template.html.twig nom logique.

Tous les services DI peuvent être définis dans app/config/config.yml (ou importés de app/config/services.yml par exemple, et toutes les classes de service peuvent aussi être de simples objets php, sans aucun lien avec le framework.

Tout ceci est fourni par défaut par le framework symfony full stack.

Là où vous aurez des problèmes, c'est lorsque vous voudrez utiliser des fichiers de traduction (comme xliff), parce qu'ils sont découverts par les bundles. sólo .

El symfony-light distribution vise à résoudre ce genre de problèmes en découvrant tout ce qui ne serait habituellement découvert que par le biais des liasses.

5voto

Reshat Belyalov Points 313

Comme cela fait déjà 5 ans, voici quelques articles supplémentaires sur les bundles Symfony.

  1. Que sont les Bundles dans Symfony ? par Iltar van der Berg.

TLDR :

Vous avez besoin de plusieurs bundles directement dans votre application ? Très probablement probablement pas. Il est préférable d'écrire un AppBundle pour éviter un spaghetti de dépendances. Vous pouvez simplement suivre la méthode meilleures pratiques et cela fonctionnera bien.

  1. Symfony : Comment regrouper par Toni Uebernickel.

TLDR :

Créez un seul bundle appelé AppBundle pour votre logique d'application. Un AppBundle - mais s'il vous plaît ne mettez pas votre logique d'application dedans !

4voto

miguel_ibero Points 518

Vous pourriez utiliser KnpRadBundle qui tente de simplifier la structure du projet.

Une autre approche consiste à utiliser src/Company/Bundle/FrontendBundle par exemple pour les liasses et src/Company/Stuff/Class.php pour les classes qui sont indépendantes de Symfony et qui pourraient être réutilisées en dehors du framework

0 votes

Mais je devrais alors coupler l'application au KnpRadBundle... N'y a-t-il pas une approche plus simple en la matière ?

1 votes

Les parties qui dépendent de symfony (contrôleurs, modèles, templates, etc...) seront toujours couplées à symfony, puisque vous l'utilisez (extension de classes, utilisation de helpers, etc...). Les classes qui fonctionnent seules seront dans l'espace de noms Company, et vous pouvez les charger en utilisant le conteneur de dépendances. Ces classes peuvent être indépendantes du framework.

1 votes

Le fait est que le concept de Bundle va directement sur le partage public. Lorsque j'écris une application, je ne veux pas partager mon code, sauf pour les parties que j'ai intentionnellement construites en tant que modules communautaires. Ai-je tort ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X