I'm trying to create a Bundle to share some services and utility classes between my different projects.
I already did this while using Symfony 5.4. But now I want to migrate to PHP 8.1 using Symfony 6.2.
I don't know what I'm doing wrong, but my web projects just don't see the services I'm creating in my Bundle.
Step by step:
I created my web project that will use the Bundle with:
symfony new my-webapp --version="6.2.*" --webapp
I created the project for the Bundle using composer.json like this:
{
"name": "carlospauluk/my-bundle",
"type": "project",
"license": "proprietary",
"require": {
"php": ">=8.1"
},
"require-dev": {
"symfony/http-kernel": "6.2.*"
},
"autoload": {
"psr-4": {
"MyBundleNamespace\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"MyBundleNamespace\\Tests\\": "tests/"
}
}
}
Inside config/services.yaml, I changed it to:
parameters:
services:
_defaults:
autowire: true
autoconfigure: true
MyBundleNamespace\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
So I created my service in src/Service:
<?php
namespace MyBundleNamespace\Service;
class NumberGeneratorService
{
public function generate(int $max) {
return random_int(0, $max);
}
}
Apparently my service is correctly configured in the Bundle, right?
After that, in my my-webapp, in composer.json I added the local repository of my my-bundle folder:
,
"repositories": [
{
"type": "path",
"url": "../my-bundle"
}
]
and then...
composer require "carlospauluk/my-bundle #dev"
Okay, I was hoping that everything was already working, and that my NumberGeneratorService service was already available in my-webapp. But not. When I run:
php bin/console debug:container MyBundleNamespace
It returns:
No services found that match "MyBundleNamespace".
What could be missing?
When I set up my bundles in my projects using Symfony 5.4, I don't remember doing anything much different than this.
Could you help me please?
Both codes are here:
https://github.com/carlospauluk/my-bundle
https://github.com/carlospauluk/my-webapp
Thanks
Finally, after almost 3 days of trying, I figured out how it should be done.
I needed to implement the loadExtension method inside the /src/MyBundle.php
<?php
namespace MyBundleNamespace;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
class MyBundle extends AbstractBundle
{
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
$container->import(__DIR__ . '/Resources/config/services.xml');
}
}
And also had to create the /src/Resources/config/services.xml file
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="my_bundle_namespace.service.number_generator_service"
class="MyBundleNamespace\Service\NumberGeneratorService">
</service>
<service id="MyBundleNamespace\Service\NumberGeneratorService" public="true"
alias="my_bundle_namespace.service.number_generator_service"/>
</services>
</container>
Without this, it doesn't work.
The Symfony Framework is excellent, but it's unfortunate that its documentation is so weak and confusing in places.
For example, at https://symfony.com/doc/current/bundles.html about the they say "This empty class is the only piece you need to create the new bundle.", which we clearly see is not true. Without implementing the loadExtension method we cannot access any services inside the bundle. If they said that clearly, it would be much easier.
Symfony's documentation is too compreehnsive, despite being very shalow at some points.
Related
I’m having trouble setting up the fixture listener for a CakePHP 4.1 project, using PHPUnit 9.4.
I’ve added the xml in phpunit.xml.dist:
<!-- Setup a listener for fixtures -->
<listeners>
<listener class="Cake\TestSuite\Fixture\FixtureInjector">
<arguments>
<object class="Cake\TestSuite\Fixture\FixtureManager"/>
</arguments>
</listener>
</listeners>
My composer.json contains the following rows:
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Test\\": "tests/",
"Cake\\Test\\": "vendor/cakephp/cakephp/tests/"
}
},
But when I run ‘phpunit.bat tests’ from the cli, it gives me the following error:
Class ‘Cake\TestSuite\Fixture\FixtureManager’ not found
Anyone knows why?
I solved it by executing the tests with the framework’s phpunit.bat, instead of the one from my local web server (xampp):
vendor/bin/phpunit.bat tests
I'm working on plugin that uses autoloader from composer. But for some reason, when I'm creating new object, PHP loads different autoloader from different plugin, and all my work is not accessible. So here is composer.json:
{
"autoload": {
"psr-4": {
"CustomizableProduct\\": "inc"
}
},
"config": {
"vendor-dir": "lib"
}
}
That's how I'm trying to load base plugin class in my_plugin.php file:
define('MY_PLUGIN_PATH', plugin_dir_path(__FILE__));
add_action('init', function () {
require_once MY_PLUGIN_PATH . "/lib/autoload.php";
new \CustomizableProduct\CustomizableProduct();
});
Every time PHP shows me error that class was not found. BUT when I'm dumping autoload with optimize-autoloader param, suddenly it works great.
Ok, I can live with that optimized autoloader, but I need to load Woocommerce classes to create custom product type. Unfortunately, changing composer.json to
"autoload": {
"psr-4": {
"CustomizableProduct\\": "inc",
"Woocommerce\\": "../woocommerce"
}
},
or with simpler, global version
"autoload": {
"psr-4": {
"CustomizableProduct\\": "inc",
"\\": "../"
}
},
not working, even with dumping autoloader with optimization param.
I have no idea what to do. Am I doing something wrong? I'm thinking about setting my own autoloader, or eventually just to create file only with include_once, including all my classes.
Just a quick question as to why I'm getting this
The autoloader expected class "App\Controller\Admin\AdminUnitController" to be defined in file "/home/glen/public_html/businessdirectory.glendev.local/vendor/composer/../../src/Controller/Admin/AdminUnitController.php". The file was found but the class was not in it, the class name or namespace probably has a typo in /home/glen/public_html/businessdirectory.glendev.local/config/services.yaml (which is loaded in resource "/home/glen/public_html/businessdirectory.glendev.local/config/services.yaml").
I have AdminUnitController.php with class name AdminUnitController. All was well until I decided I want the admin controllers in their own sub folder like this:
Controller\Admin\AdminUnitController.php
From the autoloader message, your issue is quite clear, your file is indeed where it should be but your class or namespace is wrong.
I would guess you changed the file structure but did not adapt your namespace.
Given the file src\Controller\Admin\AdminUnitController.php
Your class should look like this (pay specific attention at the namespace):
<?php
namespace App\Controller\Admin;
class AdminUnitController
{
// some code here
}
That is actually not a Symfony behavior you are facing here, but one of composer, that serves Symfony with the autoloader and that uses PSR-4 class autoloading convention.
For Reference
1) see your composer.json that have those lines:
{
// some definitions here
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
// some more definitions here
}
2) see the PSR-4 naming convention: and especially the examples in their documentation: https://www.php-fig.org/psr/psr-4/#3-examples
I have a Symfony2 project, where I have a Bundle that is generic (GenericBundle) accross multiple projects, and a site-specific Bundle (SpecificBundle) that is a child-bundle of GenericBundle.
When I am developing I want the GenericBundle to be loaded locally, from a specific directory, but when deployed I want it to behave as a normal VendorBundle.
I have setup my main composer.json in my Symfony2 project with this setting which makes it load the GenericBundle from my directory when developing:
"autoload-dev": {
"psr-0": { "": "../genericBundleDirectory/" }
},
This works fine, but how do I get the GenericBundle to load like a VendorBundle only in production-environment?
Normally you would have an src/ directory to accomodate your AppBundle and/or other bundles related to your application.
"autoload-dev": {
"psr-0": { "": "src/" }
},
I've written my own Assetic filter, contained within a Symfony2 Bundle, in order to compile CommonJS modules into a single file. It's called cjsDeliveryBundle, but let's leave that aside for now.
I want to be able to set different options on the filter from my config_dev.yml and config_prod.yml files.
The filter has a single public setter: setMinifyIdentifiers, which accepts a boolean. I read the Symfony2 documentation on setter injection and added the following to my config.yml:
assetic:
filters:
cssrewrite: ~
cjs_delivery:
resource: "%kernel.root_dir%/../src/MattCG/cjsDeliveryBundle/Resources/config/services.xml"
calls:
- [ setMinifyIdentifiers, [ true ] ]
The following is the services.xml for my Bundle.
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="matt_cg.cjs_delivery_filter.class">MattCG\cjsDeliveryBundle\Assetic\Filter\cjsDeliveryFilter</parameter>
<parameter key="matt_cg.cjs_delivery_filter.minify_identifiers">null</parameter>
</parameters>
<services>
<service id="matt_cg.cjs_delivery_filter" class="%matt_cg.cjs_delivery_filter.class%">
<tag name="assetic.filter" alias="cjs_delivery"></tag>
<call method="setMinifyIdentifiers">
<argument>%matt_cg.cjs_delivery_filter.minify_identifiers%</argument>
</call>
</service>
</services>
</container>
The filter works fine, except that the setter is never called with true as an argument. What am I doing wrong?
Got it. The solution is to have separate YAML parameters files for each environment and to specify the filter parameters in each one.
So here's what I did in my case.
Remove the - { resource: parameters.yml } from the imports: directive in config.yml.
Remove the parameters.yml file and put the parameters in two new files instead: parameters_dev.yml and parameters_prod.yml.
Add - { resource: parameters_dev.yml } to the imports: directive in config_dev.yml and - { resource: parameters_prod.yml } to the imports: directive in config_prod.yml.
Add matt_cg.cjs_delivery_filter.minify_identifiers: true to the parameters: directive in parameters_prod.yml.