Symfony'de Controller yerine Action sınıfını kullanın

Aug 23 2020

Ben yapışık am Eylem Sınıf kullanmak yerine yaklaşımı Kontrolörü . Açıklama çok basittir: Denetleyici çok sayıda eylem içerir, Bağımlılık Ekleme ilkesini takip ederken gerekli tüm bağımlılıkları bir kurucuya aktarmamız gerekir ve bu, Denetleyicinin çok sayıda bağımlılığa sahip olduğu, ancak belirli bir zamanda bir durum yaratır . (örneğin istek) sadece bazı bağımlılıkları kullanıyoruz. Bu spagetti kodunu sürdürmek ve test etmek zor.

Açıklığa kavuşturmak için, Zend Framework 2'de zaten bu yaklaşımla çalışıyordum, ancak burada Middleware olarak adlandırılıyor . API-Platform'da, Controller yerine Action sınıfını da kullandıkları benzer bir şey buldum , ancak sorun şu ki, onu nasıl pişireceğimi bilmiyorum.

UPD: Bir sonraki Eylem Sınıfını nasıl edinebilirim ve standart Denetleyiciyi nasıl değiştirebilirim ve normal Symfony projesine hangi yapılandırmayı eklemeliyim?

<?php
declare(strict_types=1);

namespace App\Action\Product;

use App\Entity\Product;
use Doctrine\ORM\EntityManager;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class SoftDeleteAction
{
    /**
     * @var EntityManager
     */
    private $entityManager; /** * @param EntityManager $entityManager
     */
    public function __construct(EntityManager $entityManager) { $this->entityManager = $entityManager; } /** * @Route( * name="app_product_delete", * path="products/{id}/delete" * ) * * @Method("DELETE") * * @param Product $product
     *
     * @return Response
     */
    public function __invoke(Request $request, $id): Response
    {
        $product = $this->entityManager->find(Product::class, $id); $product->delete();
        $this->entityManager->flush();

        return new Response('', 204);
    }
}

Yanıtlar

3 Cerad Aug 23 2020 at 21:45

Soru stackoverflow için biraz belirsiz olsa da biraz ilginç. İşte bazı yapılandırma ayrıntıları.

Kullanıma hazır S4 iskelet projesiyle başlayın:

symfony new --version=lts s4api
cd s4api
bin/console --version # 4.4.11
composer require orm-pack

SoftDeleteAction'ı ekleyin

namespace App\Action\Product;
class SoftDeleteAction
{
    private $entityManager; public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }
    public function __invoke(Request $request, int $id) : Response
    {
        return new Response('Product ' . $id);
    }
}

Ve rotayı tanımlayın:

# config/routes.yaml
app_product_delete:
    path: /products/{id}/delete
    controller: App\Action\Product\SoftDeleteAction

Bu noktada kablolama neredeyse tamamlanmıştır. URL'ye giderseniz:

The controller for URI "/products/42/delete" is not callable:

Bunun nedeni, hizmetlerin varsayılan olarak özel olmasıdır. Normalde, hizmeti herkese açık hale getirmekle ilgilenen AbstractController'dan uzanırsınız, ancak bu durumda en hızlı yaklaşım, eylemi bir denetleyici olarak etiketlemektir:

# config/services.yaml
    App\Action\Product\SoftDeleteAction:
        tags: ['controller.service_arguments']

Bu noktada, çalışan bir kablolu eyleminiz olmalıdır.

Elbette birçok varyasyon ve birkaç ayrıntı daha var. Rotayı POST veya sahte SİLME ile sınırlandırmak isteyeceksiniz.

Ayrıca, boş bir ControllerServiceArgumentsInterface eklemeyi ve ardından denetleyici hizmetlerini artık manuel olarak tanımlamanıza gerek kalmaması için denetleyici etiketini uygulamak için hizmetler örnek işlevini kullanmayı da düşünebilirsiniz.

Ancak bu, başlamanız için yeterli olmalıdır.

1 SerhiiPopov Sep 24 2020 at 18:38

Uygulamaya çalıştığım yaklaşım ADR kalıbı (Action-Domain-Responseder) olarak adlandırıldı ve Symfony bunu zaten 3.3 sürümünden başlattı. Buna Invokable Controllers olarak başvurabilirsiniz .

Resmi belgelerden:

Denetleyiciler ayrıca, ADR modelini (Eylem-Etki Alanı-Yanıtlayıcı) izlerken yaygın bir uygulama olan __invoke () yöntemini kullanarak tek bir eylem tanımlayabilir:

// src/Controller/Hello.php
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route("/hello/{name}", name="hello")
 */
class Hello
{
    public function __invoke($name = 'World') { return new Response(sprintf('Hello %s!', $name));
    }
}