首页 > 解决方案 > Symfony 5服务未加载

问题描述

我有一个类用于上传文件作为服务,如 symfony 文档。 https://symfony.com/doc/current/controller/upload_file.html#creating-an-uploader-service

我使用 symfony 5。

当我在 main config/services.yaml 中声明服务时,这项工作。

但是我有一个用于文件管理的包,我想将服务声明放在这个包中:App/AD/ExplorerBundle/Resources/config/services.yaml。

当我这样做时,这不再起作用。

我有错误

无法解析“App\AD\ExplorerBundle\Controller\FileController::addAction()”的参数 $fileUploader:无法自动连接服务“App\AD\ExplorerBundle\Service\FileUploader”:方法“__construct()”的参数“$targetDirectory”是类型提示的“字符串”,您应该明确配置其值。

我不明白为什么,因为 _defaults autoconfigure 和 autowire = true

我测试缓存:清除,重新加载服务器,但没有任何效果。

任何帮助将不胜感激

编辑:我的捆绑扩展:在 AD\ExplorerBundle\DependencyInjection

<?php

namespace App\AD\ExplorerBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

/**
 * This is the class that loads and manages your bundle configuration.
 *
 * @link http://symfony.com/doc/current/cookbook/bundles/extension.html
 */
class ADExplorerExtension extends Extension
{
    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');
    }
}

我的捆绑服务:在 AD\ExplorerBundle\Service

<?php
namespace App\AD\ExplorerBundle\Service;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\String\Slugger\SluggerInterface;

class FileUploader
{
    private $targetDirectory;
    private $slugger;

    public function __construct(string $targetDirectory, SluggerInterface $slugger)
    {
        $this->targetDirectory = $targetDirectory;
        $this->slugger = $slugger;
    }

    public function upload(UploadedFile $file): array
    {
        $originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
        $safeFilename = $this->slugger->slug($originalFilename);
        $fileName = $safeFilename.'-'.uniqid().'.'.$file->guessExtension();

        $result = array(
            'filename' => $fileName,
            'originalName' => $originalFilename,
            'extension' => $file->guessExtension()
                );

        try {
            $file->move($this->getTargetDirectory(), $fileName);
        } catch (FileException $e) {
            // ... handle exception if something happens during file upload
        }

        return $result;
    }

    public function getTargetDirectory()
    {
        return $this->targetDirectory;
    }
}

我的配置/services.yaml

parameters:
    locale: 'fr'
    doctrine_behaviors_translatable_fetch_mode: "LAZY"
    doctrine_behaviors_translation_fetch_mode: "LAZY"



imports:
    - { resource: '@ADCoreBundle/Resources/config/services.yml' }
    - { resource: './parameters.yaml' }

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'


    App\Controller\:
        resource: '../src/Controller'
        tags: ['controller.service_arguments']

我的捆绑服务:在 AD\ExplorerBundle\Resources\config\service.yaml

parameters:
    brochures_directory: '%kernel.project_dir%/public/uploads'

services: 
    ad_file_uploader:         
        class: App\AD\ExplorerBundle\Service\FileUploader
        arguments:
            $targetDirectory: '%brochures_directory%'

我阅读了文档: https ://symfony.com/doc/current/bundles/extension.html

https://symfony.com/doc/current/service_container.html#manually-wiring-arguments

https://symfony.com/doc/current/service_container/autowiring.html

我真的不明白这一点:

公共和可重用的捆绑包¶

公共包应该明确地配置他们的服务,而不是依赖于自动装配。自动装配取决于容器中可用的服务,并且捆绑包无法控制它们所包含的应用程序的服务容器。您可以在公司内部构建可重用捆绑包时使用自动装配,因为您可以完全控制所有代码。

我认为这没关系,因为它是我的捆绑包和我的应用程序,所以我可以完全控制代码。

所以,我测试了很多东西,但没有任何效果。

如果有人有想法谢谢

标签: symfony

解决方案


Symfony 建议在 bundle 中使用手动服务定义,主要是为了避免每次重建缓存时不断扫描 bundle 的开销。但我认为看看使用 autowire/autoconfigure 创建一个可能可重用的捆绑包实际上需要什么可能会很有趣。尝试尽可能多地遵循捆绑包的最佳实践

为了我自己的参考,我签入了一个完整的工作包示例

最终,bundle 应该在它们自己的存储库中结束。但是,通过将捆绑包包含在应用程序中来开发捆绑包会更容易。这是我使用的方法。但重要的是不要尝试将您的应用程序源代码与捆绑源代码混合。这样做不仅具有挑战性,而且会使将您的捆绑包复制到它自己的存储库中变得困难。

所以我们从这样的目录结构开始:

project # Symfony application project
    src: # Application source code
    explorer-bundle # AD Explorer Bundle source code
        ADExplorerBundle.php

正确设置命名空间也很重要。前缀确实应该是唯一的供应商标识符,以避免可能的命名冲突。在这种情况下,我们使用 AD。当然,由于 AD 可能有多个捆绑包,我们进一步细分为捆绑包特定标识符。

namespace AD\ExplorerBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class ADExplorerBundle extends Bundle
{

}

为了使自动加载工作,我们调整了 composer.json 文件。一旦包被转换成真正的作曲家包,这条线将不再需要。当然,您必须将 bundle 类添加到 project/config/bundles.php

# composer.json
    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "AD\\ExplorerBundle\\": "explorer-bundle/"
        }
    },
# run composer "dump-autoload" after making this change

所以现在我们需要一个扩展来加载包的 services.yaml。这只是一个标准负载,因此无需在此处显示代码。这就是 services.yaml 文件的样子:

# explorer-bundle/Resources/config/services.yaml
parameters:
    # Cheating here, this would normally be part of the bundle configuration
    ad_explorer_target_directory: 'some_directory'

services:
    _defaults:
        autowire: true
        autoconfigure: true
        bind:
            $targetDirectory: '%ad_explorer_target_directory%'

    AD\ExplorerBundle\:
        resource: '../../*'
        exclude: '../../{DependencyInjection,Entity,Migrations,Tests,ADExplorerBundle.php}'

    AD\ExplorerBundle\Controller\:
        resource: '../../Controller'
        tags: ['controller.service_arguments']

为了简单起见,我只是将目标目录作为参数。您可能想要进行实际的捆绑配置并让应用程序能够设置它。但这超出了这个答案的范围。

为了测试,我选择创建一个命令。我发现它比尝试刷新浏览器更容易。

// Easier to debug an injected service using a command than a controller
class ExplorerAddCommand extends Command
{
    private $fileUploader;

    protected static $defaultName = 'ad:explorer:add';

    public function __construct(FileUploader $fileUploader)
    {
        parent::__construct();
        $this->fileUploader = $fileUploader;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        echo "Explorer Add " . $this->fileUploader->targetDirectory . "\n";
        return 0;
    }
}

就是这样。


推荐阅读