我想將 symfony2.8 api 遷移到 symfony5.4。我轉移了我所有的 Symfony 命令,更改containerAwareCommand為\Symfony\Component\Console\Command\Command并更改命令配置以匹配新版本的配置。
bin/console但是,我的命令在串列中仍然不可見,如果不在services.yml. 我有 30 多個命令要轉移并在其中添加一段在service.yml我看來乏味且沒有必要。我是不是忘記了什么?
第二個問題,如何在命令中直接訪問服務容器?在 Symfony 2.8 上,我正在使用它來訪問它,$this->getContainer()->get('toto')但它似乎不再適用于版本 5。
服務.yml:
tags:
- { name: 'console.command', command: 'word:manager:expiration_email' }
arguments:
- '@service_container'
- '@logger'
命令:
protected function configure()
{
$this
->setName('word:manager:expiration_email')
->setDescription('Alert email : Send an email x days before the expiration.')
->addOption(
'days',
'd',
InputOption::VALUE_REQUIRED,
'How many days before the expiration',
1
);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$days = intval($input->getOption('days'));
if ($days > 0) {
try {
$this->getManager()->sendExpirationEmail($days);
} catch (\Exception $e) {
$this->container->get('logger')->error(sprintf(
'aemCommand: an error occurred in file "%s" (L%d): "%s"',
$e->getFile(),
$e->getLine(),
$e->getMessage()
));
}
}
return 0;
}
/**
* @return aem
*/
private function getManager()
{
return $this->container->get('word.manager.alert_employee');
}
服務.yml:
imports:
- { resource: "@WORDCoreBundle/Resources/config/services.yml" }
- { resource: "@WORDAlertBundle/Resources/config/services.yml" }
- { resource: "@WORDEmployeeBundle/Resources/config/services.yml" }
- { resource: "@WORDTimeManagementBundle/Resources/config/services.yml" }
- { resource: "@WORDUserBundle/Resources/config/services.yml" }
- { resource: "@WORDFileBundle/Resources/config/services.yml" }
- { resource: "@WORDLocalisationBundle/Resources/config/services.yml" }
- { resource: "@WORDPlatformBundle/Resources/config/services.yml" }
- { resource: "@WORDCompanyBundle/Resources/config/services.yml" }
- { resource: "@WORDContractBundle/Resources/config/services.yml" }
- { resource: "@WORDFolderBundle/Resources/config/services.yml" }
- { resource: "@WORDTrainingBundle/Resources/config/services.yml" }
- { resource: "@WORDExpenseReportBundle/Resources/config/services.yml" }
- { resource: "@WORDBookStoreBundle/Resources/config/services.yml" }
- { resource: "@WORDPaymentBundle/Resources/config/services.yml" }
- { resource: "@WORDInvoiceBundle/Resources/config/services.yml" }
- { resource: "@WORDAgendaBundle/Resources/config/services.yml" }
- { resource: "@WORDAlertBundle/Resources/config/services.yml" }
- { resource: "@WORDAnnualReviewBundle/Resources/config/services.yml" }
- { resource: "@WORDProReviewBundle/Resources/config/services.yml" }
- { resource: "@WORDWidgetBundle/Resources/config/services.yml" }
- { resource: "@WORDCommentBundle/Resources/config/services.yml"}
- { resource: "@WORDFrontBundle/Resources/config/services.yml" }
- { resource: "@WORDNotificationBundle/Resources/config/services.yml" }
services:
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
_defaults:
autowire: true
autoconfigure: true
WORD\CoreBundle\Controller\ApiController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\PlatformBundle\Controller\PlatformRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\UserBundle\Controller\UserRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\AlertBundle\Controller\AlertRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\EmployeeBundle\Controller\EmployeeRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\CompanyBundle\Controller\CompanyRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\CompanyBundle\Controller\ContactRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\CompanyBundle\Controller\ServiceRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\CompanyBundle\Controller\ChartRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD\BookStoreBundle\Controller\InfoRESTController:
calls:
- method: setContainer
arguments: ['@service_container']
WORD.user.manager.user:
class: 'WORD\UserBundle\Service\UserManager'
public: false
lazy: true
arguments:
- '@fos_user.util.password_updater'
- '@fos_user.util.canonical_fields_updater'
- '@fos_user.object_manager'
- 'WORD\UserBundle\Entity\User'
WORD\AgendaBundle\DataFixtures\AgendaFixtures:
tags: [doctrine.fixture.orm]
WORD\UserBundle\DataFixtures\UserFixtures:
tags: [doctrine.fixture.orm]
arguments:
$fos_manager: '@fos_user.user_manager'
WORD\LocalisationBundle\DataFixtures\CountryFixtures:
tags: [doctrine.fixture.orm]
App\DataFixtures\Processor\UserProcessor:
tags: [ { name: fidry_alice_data_fixtures.processor } ]
WORD\AlertBundle\Command\AlertExpirationEmailCommand:
tags:
- { name: 'console.command', command: 'WORD:alert:expiration_email' }
arguments:
- '@service_container'
- '@logger'
作曲家.json
"autoload": {
"psr-4": {
"App\\": "src/",
"WORD\\": "WORD"
}
},
uj5u.com熱心網友回復:
命名空間 PSR-4 映射
為了讓 composer 正確地將你的檔案映射到它們關聯的命名空間,命名空間和路徑必須在autoload.psr-4和/或autoload.classmapcomposer.json 配置中宣告。
composer.json 自動加載映射
"autoload": {
"psr-4": {
"App\\": "src/",
"W2D\\": "W2D/",
"WORD\\": "WORD/"
},
"classmap": {
"src/",
"W2D/",
"WORD/"
}
},
將命令轉換為延遲加載
首先為了減少配置和實體化開銷,通過使用protected static $defaultName = '.....'.
對命名空間中的每個命令重復更改。
命令更改
// /WORD/AlertBundle/Command/AlertExpirationEmailCommand.php
namespace WORD\AlertBundle\Command;
class AlertExpirationEmailCommand extends Command
{
protected static $defaultName = 'WORD:alert:expiration_email';
protected function configure()
{
$this
/* ->setName('WORD:alert:expiration_email') */
// ...
}
}
修復服務加載
Symfony 3.4 中public: false的所有服務默認情況下(包括Logger)使它們無法通過使用container->get('service'). 此外,如果服務未顯式注入服務配置或通過autowire,則服務將從容器中洗掉以減少開銷。因此,您永遠不應該將整個容器注入到服務中——因為您可能正在尋找的服務可能已被無意中洗掉。
由于服務的處理方式,_defaults應該是任何服務宣告的第一行。同樣重要的是要注意,這_defaults不會被任何其他檔案或配置匯入繼承/級聯。
由于App\默認命名空間是自動裝配的,因此您需要將其替換為您的命名空間,以確保將適當的命名空間作為自動裝配服務加載。
應用服務自動裝配
# /config/services.yaml
services:
# default configuration for services in *this* file
_defaults:
autowire: true
autoconfigure: true
# makes classes in App/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# makes classes in WORD/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
WORD\: # <--- root namespace to autowire eg: WORD\Command\MyCommand
resource: '../WORD/' # location of the namespace
exclude:
- '../WORD/DependencyInjection/'
- '../WORD/Entity/'
- '../WORD/*Bundle/' # Never autowire Bundles as they should be self-contained
# declare manually wired and overridden services below this line
# ...
將捆綁配置修復為自包含
隨著 Symfony Flex (4.0 ) 中自動裝配的引入和操作順序的更改,在主應用程式中加載 Bundle 服務config/services.yaml通常會導致加載沖突。因此,最好使用Extensionfor each bundle來加載服務配置,直到您從Bundle檔案層次結構遷移到有利于使用平面應用程式配置為止。
再次為每個注冊的 Bundle 重復更改。
最終,最好的做法是明確定義捆綁包中的所有服務,而不是使用
autowire: true. 但是,考慮到這是從 2.8 到 5.x 的遷移,您將希望不再使用 Bundle 進行重構。
捆綁 services.yaml 和 autowire
# /WORD/AlertBundle/Resources/config/services.yml
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.
# makes classes in WORD/AlertBundle/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
WORD\AlertBundle\:
resource: '../../*'
exclude:
- '../../DependencyInjection/'
- '../../Entity/'
# Bundle specific services below this line
# ...
捆綁擴展
/* /WORD/AlertBundle/DependencyInjection/AcmeHelloExtension.php */
namespace WORD\AlertBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
class WORDAlertExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader(
$container,
new FileLocator(__DIR__ . '/../Resources/config')
);
$loader->load('services.yml');
}
}
手動加載 Bundle 擴展
當不使用 Bundle 檔案層次結構命名約定時,您可能需要在Bundle::getContainerExtension()方法中手動實體化 Bundle 的擴展。
/* /WORD/AlertBundle/WordAlertBundle.php */
namespace WORD\AlertBundle;
// ...
use WORD\AlertBundle\DependencyInjection\AlertExtension;
class WordAlertBundle extends Bundle
{
public function getContainerExtension()
{
return new AlertExtension();
}
}
容器訪問和自動裝配
如上所述,從 Symfony 3.4 開始,您永遠不應該將整個容器直接注入到服務中。
相反,所需的服務應該在__construct()類的方法中顯式地型別提示,這將被注入autowire: true服務配置。
# EXAMPLE ONLY
#
# /WORD/AlertBundle/Resources/config/services.yml
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true
# example autowire and autoconfig
WORD\AlertBundle\:
resource: '../../*'
# EXAMPLE - Service Override Declaration
WORD\AlertBundle\Service\EmployeeManager:
arguments:
- '@fos_user.util.password_updater'
- '@fos_user.util.canonical_fields_updater'
- '@fos_user.object_manager'
- 'WORD\UserBundle\Entity\User'
# EXAMPLE - declaration of alias
word.manager.alert_employee:
alias: WORD\AlertBundle\Service\EmployeeManager
public: true # to prevent deletion of service when not used
# EXAMPLE OVERRIDE - manually declare all files under Command as a command service
WORD\AlertBundle\Command\:
resource: '../../Command/*'
tags:
- { name: 'console.command' } # force as a command
// /WORD/AlertBundle/Command/AlertExpirationEmailCommand.php
namespace WORD\AlertBundle\Command;
// ...
use Psr\Log\LoggerInterface;
use WORD\AlertBundle\Service\EmployeeManager;
class AlertExpirationEmailCommand extends Command
{
protected static $defaultName = 'WORD:alert:expiration_email';
private LoggerInterface $logger;
private EmployeeManager $manager;
public function __construct(LoggerInterface $logger, EmployeeManager $manager)
{
$this->logger = $logger;
$this->manager = $manager;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$days = intval($input->getOption('days'));
if ($days > 0) {
try {
$this->getManager()->sendExpirationEmail($days);
} catch (\Exception $e) {
$this->logger->error(sprintf(
'aemCommand: an error occurred in file "%s" (L%d): "%s"',
$e->getFile(),
$e->getLine(),
$e->getMessage()
));
}
}
return 0;
}
/**
* @return EmployeeManager
*/
private function getManager(): EmployeeManager
{
return $this->manager;
}
}
向前進
遍歷每個直接使用容器的服務類,并重構它們以在各自的 services.yml 配置中使用顯式服務型別提示或手動連接。
您的目標應該是從 2.x - 3.4 Bundle 檔案層次結構和命名約定以及埠遷移到平面配置,例如遷移WORD\AlertBundle到WORD\Alert\...或WORD\Command\Alert\.... 這樣,您就擁有了一個單一的內聚應用程式,而不是幾個作為 Bundle 的小型應用程式,從而顯著降低了代碼復雜性、開銷、頁面加載和快取預熱時間。
或者,如果每個 Bundle 提供在多個應用程式之間共享的功能,請按照Bundle 最佳實踐將每個 Bundle 移動到它們自己的存盤庫,以便它們與主應用程式隔離。這將需要另一個目錄重構。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/517425.html
上一篇:一對多關系問題
