Mọi thứ có thực sự là một gói trong Symfony 2.x không?


205

Tôi biết các câu hỏi như thế này , nơi mọi người có xu hướng thảo luận về khái niệm chung của Symfony 2 về gói.

Vấn đề là, trong một ứng dụng cụ thể, chẳng hạn như một ứng dụng giống như twitter, mọi thứ có thực sự nằm trong một gói chung chung, như các tài liệu chính thức nói không?

Lý do tôi hỏi điều này là vì khi chúng tôi phát triển các ứng dụng, nói chung, chúng tôi không muốn kết hợp mã của chúng tôi với một số khung keo đầy đủ.

Nếu tôi phát triển ứng dụng dựa trên Symfony 2 và, tại một số điểm, tôi quyết định Symfony 2 không thực sự là lựa chọn tốt nhất để duy trì sự phát triển , đó có phải là vấn đề với tôi không?

Vì vậy, câu hỏi chung là: tại sao mọi thứ là một bó là một điều tốt?

EDIT # 1

Gần một năm kể từ khi tôi hỏi câu hỏi này, tôi đã viết một bài viết để chia sẻ kiến ​​thức của mình về chủ đề này.


1
Đây chỉ là một nhận xét, không phải là một câu trả lời. Cá nhân tôi nghĩ rằng, chúng ta nên chọn khung cẩn thận trước khi bắt đầu dự án. Mỗi khung công tác có cách riêng để làm công cụ, vì vậy nó sẽ cung cấp các công cụ để hỗ trợ theo cách đó tốt nhất. Nếu chúng ta thích cách đó, chúng tôi làm theo. Có những lựa chọn khác ngoài kia. Chúng tôi không muốn sử dụng một con dao để cắt gỗ thay vì cưa. Nhưng đó là một câu hỏi rất thú vị mà bạn đặt ra :)
Anh Nguyễn

Câu trả lời:


219

Tôi đã viết một bài đăng blog kỹ lưỡng và cập nhật hơn về chủ đề này: http://elnur.pro/symfony-without-bundles/


Không, không phải tất cả mọi thứ phải ở trong một bó. Bạn có thể có một cấu trúc như thế này:

  • src/Vendor/Model - cho các mô hình,
  • src/Vendor/Controller - cho bộ điều khiển,
  • src/Vendor/Service - Cho dịch vụ,
  • src/Vendor/Bundle- cho các gói, như src/Vendor/Bundle/AppBundle,
  • Vân vân.

Bằng cách này, bạn sẽ đưa vào thứ AppBundleduy nhất thực sự là Symfony2 cụ thể. Nếu bạn quyết định chuyển sang một khung công tác khác sau đó, bạn sẽ thoát khỏi Bundlekhông gian tên và thay thế nó bằng các công cụ khung đã chọn.

Xin lưu ý rằng những gì tôi đề xuất ở đây là dành cho mã cụ thể của ứng dụng . Đối với các gói có thể tái sử dụng, tôi vẫn đề nghị sử dụng các thực tiễn tốt nhất .

Giữ các thực thể ra khỏi gói

Để giữ các thực thể src/Vendor/Modelbên ngoài bất kỳ gói nào, tôi đã thay đổi doctrinephần trong config.ymltừ

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

đến

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

Tên của các thực thể - để truy cập từ kho lưu trữ của Học thuyết - bắt đầu bằng Modeltrong trường hợp này, ví dụ , Model:User.

Ví dụ, bạn có thể sử dụng tên miền con để nhóm các thực thể liên quan với nhau src/Vendor/User/Group.php. Trong trường hợp này, tên của thực thể là Model:User\Group.

Giữ bộ điều khiển ra khỏi gói

Trước tiên, bạn cần nói với JMSDiExtraBundle để quét srcthư mục cho các dịch vụ bằng cách thêm phần này vào config.yml:

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

Sau đó, bạn xác định bộ điều khiển là dịch vụ và đặt chúng dưới Controllerkhông gian tên:

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

Lưu ý rằng tôi đang sử dụng Elnur AbTHERControllBundle của mình để đơn giản hóa việc xác định bộ điều khiển là dịch vụ.

Điều cuối cùng còn lại là bảo Symfony tìm kiếm các mẫu không có gói. Tôi làm điều này bằng cách ghi đè dịch vụ đoán mẫu, nhưng vì cách tiếp cận khác nhau giữa Symfony 2.0 và 2.1, tôi đang cung cấp các phiên bản cho cả hai.

Ghi đè trình đoán mẫu Symfony 2.1+

Tôi đã tạo ra một gói làm điều đó cho bạn.

Ghi đè trình nghe mẫu Symfony 2.0

Đầu tiên, xác định lớp:

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

Và sau đó nói với Symfony sử dụng nó bằng cách thêm nó vào config.yml:

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

Sử dụng các mẫu không có gói

Bây giờ, bạn có thể sử dụng các mẫu trong số các gói. Giữ chúng trong app/Resources/viewsthư mục. Ví dụ: các mẫu cho hai hành động đó từ trình điều khiển mẫu ở trên được đặt trong:

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

Khi đề cập đến một mẫu, chỉ cần bỏ qua phần gói:

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

2
Đó thực sự là một cách tiếp cận thực sự thú vị. Cùng với đó, tôi cũng có thể phát triển các gói thực có chứa bộ tính năng cụ thể mà cộng đồng có thể sử dụng, mà không khó kết nối ứng dụng của tôi với chính khung công tác.
Daniel Ribeiro

57
Để làm cho mã bạn chia sẻ với cộng đồng không được ghép nối với Symfony2, bạn có thể đặt nội dung chung vào thư viện và sau đó tạo một gói tích hợp thư viện đó với Symfony2.
Elnur Abdurrakhimov

9
Đây là một ý tưởng thú vị miễn là bạn không dựa vào bất kỳ lệnh tạo mã nào. generate:doctrine:crudví dụ, mong muốn thực thể (= model trong trường hợp của elnur) nằm trong một gói để hoạt động.
geca

2
Với cách tiếp cận này, có cách nào để lấy lại chức năng của giao diện ứng dụng / bảng điều khiển CLI không? Tôi thích ý tưởng giữ các mô hình của mình ở một vị trí bên ngoài bất kỳ gói nào, nhưng tôi muốn giữ quyền truy cập vào chức năng CLI.
Andy Baird

3
Điều này nên được đưa vào một gói :)
d0001

20

Tất nhiên bạn có thể tách rời ứng dụng của bạn. Chỉ cần phát triển nó như một thư viện và tích hợp nó vào symfony vendor/-folder (bằng cách sử dụng depshoặc composer.json, tùy thuộc vào thời điểm bạn sử dụng Symfony2.0 hoặc Symfony2.1). Tuy nhiên, bạn cần ít nhất một gói, hoạt động như "lối vào" của thư viện của bạn, nơi Symfony2 tìm thấy bộ điều khiển (và như vậy).


2
Vì thẻ symfony-2.0tôi sẽ cho rằng bạn sử dụng phiên bản 2.0 hiện tại. Trong trường hợp này, hãy tạo một kho lưu trữ git bất cứ nơi nào bạn thích và đặt mọi thứ vào đó, những gì bạn muốn phát triển độc lập khỏi symfony. Trong bản cập nhật dự án symfony của bạn, depstệp của bạn như được đề cập ở đây symfony.com/doc/civerse/cookbook/workflow/iêu Sau đó, chỉ cần tạo một (hoặc nhiều) gói ứng dụng ( php app/console generate:bundle) cho các công cụ dành riêng cho symfony.
KingCrunch

11

Một bản phân phối symfony thông thường có thể hoạt động mà không cần bất kỳ gói (ứng dụng) bổ sung nào, tùy thuộc vào số lượng chức năng bạn muốn sử dụng từ khung stack đầy đủ.

Ví dụ, bộ điều khiển của bạn có thể là bất kỳ cuộc gọi nào có thể được đặt ở bất kỳ đâu trong cấu trúc dự án của bạn, ngay khi chúng được tự động tải.

Trong tệp định nghĩa định tuyến, bạn có thể sử dụng:

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

Nó có thể là bất kỳ đối tượng php cũ đơn giản nào, chỉ được gắn với khung bởi thực tế nó phải trả về Symfony\Component\HttpFoundation\Response đối tượng.

Các mẫu twig của bạn (hoặc các mẫu khác) có thể được đặt như thế app/Resources/views/template.html.twigvà có thể được hiển thị bằng cách sử dụng::template.html.twig tên logic.

Tất cả các dịch vụ DI có thể được xác định trong app / config / config.yml (hoặc được nhập từ app/config/services.yml ví dụ và tất cả các lớp dịch vụ cũng có thể là bất kỳ đối tượng php cũ đơn giản nào. Không bị ràng buộc với khung công tác nào cả.

Tất cả điều này được cung cấp theo mặc định bởi khung ngăn xếp đầy đủ symfony.

Trong trường hợp bạn sẽ có vấn đề là khi bạn sẽ muốn sử dụng file dịch (như xliff), bởi vì chúng được phát hiện thông qua bó chỉ .

Các symfony nhẹ mục tiêu phân phối để giải quyết các loại vấn đề bằng cách khám phá tất cả những gì sẽ được thường phát hiện chỉ thông qua bó.


5

Bạn có thể sử dụng KnpRadBundle , để cố gắng đơn giản hóa cấu trúc dự án.

Một cách tiếp cận khác là sử dụng src/Company/Bundle/FrontendBundleví dụ cho các gói và src/Company/Stuff/Class.phpcho các lớp độc lập với symfony và có thể được sử dụng lại bên ngoài khung


Nhưng sau đó tôi sẽ ghép ứng dụng với KnpRadBundle ... Không có cách tiếp cận nào dễ dàng hơn trong vấn đề này?
Daniel Ribeiro

1
Các phần phụ thuộc vào symfony (Bộ điều khiển, Mô hình, mẫu, v.v.) sẽ luôn được ghép với symfony, vì bạn đang sử dụng nó (mở rộng các lớp, sử dụng trợ giúp, v.v ...). Các lớp hoạt động một mình sẽ nằm trong không gian tên Công ty và bạn có thể tải chúng bằng cách sử dụng bộ chứa phụ thuộc. Các lớp này có thể độc lập khung.
Miguel_ibero

1
Vấn đề là, khái niệm Bundleđi trực tiếp vào chia sẻ công khai. Khi tôi viết một số ứng dụng, tôi không muốn chia sẻ mã của mình, ngoại trừ những phần mà tôi cố tình xây dựng dưới dạng các mô-đun hướng đến cộng đồng. Liệu tôi có sai?
Daniel Ribeiro

Bạn không phải chia sẻ các gói. Hãy nghĩ về một gói như là một nhóm các lớp với một số cấu hình. Trong mỗi dự án bạn có thể có các gói khác nhau.
Miguel_ibero

Bạn nên đọc cuốn sách symfony
Miguel_ibero

5

Vì đã 5 năm trôi qua, đây là một vài bài viết về Gói Symfony.

  1. Gói trong Symfony là gì? bởi Iltar van der Berg.

TLDR:

Bạn có cần nhiều gói trong ứng dụng của bạn trực tiếp? Hầu như không. Tốt hơn hết là bạn nên viết một AppBundle để ngăn chặn sự phụ thuộc vào spaghetti. Bạn chỉ có thể làm theo các thực tiễn tốt nhất và nó sẽ hoạt động tốt.

  1. Symfony: Cách gói bởi Toni Uebernickel.

TLDR:

Chỉ tạo một gói có tên là AppBundle cho logic ứng dụng của bạn. Một AppBundle - nhưng vui lòng không đặt logic ứng dụng của bạn vào đó!


-2

Khung Symfony rất tốt để nhanh chóng khởi chạy một bằng chứng về khái niệm và tất cả các mã có thể nhập trong ứng dụng gói mặc định trong src /

Trong gói này, bạn có thể cấu trúc mã của mình như bạn muốn.

Sau khi bạn muốn sử dụng công nghệ khác để phát triển POC của mình, bạn có thể dễ dàng dịch nó bởi vì bạn không cấu trúc tất cả mã của mình theo khái niệm bó.

Đối với tất cả các khái niệm bạn không cực đoan này. Gói là tốt nhưng bó mọi thứ và hàng ngày là không tốt.

Có lẽ bạn có thể sử dụng Silex (khung vi mô Symfony) để phát triển Proof of Concept để giảm tác động của bên thứ ba.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.