Symfony 4 controllers in sub folders producing error - symfony

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

Related

Custom Bundle with Symfony 6.2

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.

Multiple different composer autoloaders in plugins, but only one autoloader is really working

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.

Extending SilverStripe modules extension

I have installed a particular module in my SilverStripe installation. The following is the directory structure
- Root
- framework
- cms
- mymodule
- code
- extensions
- CustomClass.php
Here is an example of CustomClass.php
class CustomClass extends Extension {
public function init() {
}
public function customMethod() {
}
}
I need to override the customMethod method of the CustomClass class. I could easily change this method, but changing here will cause trouble in the future if the modules get updated. All the changes made will be lost.
So for this I want to extend the extension class used in modules.
I have created an extension /mysite/extensions/MyCustomClass.php
class MyCustomClass extends Extension {
public function customMethod() {
//do my code here
}
}
but I have no idea how to apply this. I thought CustomClass::add_extension("MyCustomClass ") but surely this will not work because add_extension method doesn't exist in CustomClass.
How do we cope with this scenario? Can I use Injector instead? If yes, how can it be called in mysite/_config.php instead of _config.yml?
Using injector does solve the problem but have to use _config.yml as well. Here is what I did.
File /mysite/extensions/MyCustomClass.php
class MyCustomClass extends CustomClass {
public function customMethod() {
//do my code here
}
}
in /mysite/_config/config.yml I added following lines
Injector:
CustomClass:
class: MyCustomClass
And in /mysite/_config.php I added following line
$object = Injector::inst()->create('CustomClass');
And it all worked fine.
There is another way to achieve similar functionality without straight up replacing a previous extension. With SilverStripe's extension system, we can control not only what configuration settings are loaded but the order they are loaded. This is important to note because the customMethod function from an extension, it uses the first one it finds from all the extensions loaded.
Because of this, it can be only a matter of controlling when your MyCustomClass extension is loaded so you can have your own customMethod function run.
Let's say the "MyModule" module has the following YAML file defined:
---
Name: MyModuleExtensions
After:
- 'framework/*'
- 'cms/*'
---
Page:
extensions:
- CustomClass
All we need to do is specify a separate YAML file to run before this "MyModule" one. This can be accomplished like:
---
Name: MyCustomModule
Before:
- MyModule/#MyModuleExtensions
---
Page:
extensions:
- MyCustomClass
Now, when you call your customMethod function on whatever class you have your extensions on (so in my example, the Page class), it will call the one from your MyCustomClass extension.

How do I structure my Symfony2 project with private Bundles?

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/" }
},

Use wiredep to inject custom CSS

I need to inject custom CSS files everytime they are created after being compiled by gulp-less. So I tried to use wiredep with custom configurations, but without success.
I changed the tag from 'bower:css' to 'custom:css', specifically to my custom task. The bower:css for default wiredep injection is still there. But after run myinjection task nothing is injected even running the task without errors.
Another weird thing, when I run my task, the files injected by wiredep (the default) disappear.
What I'm missing?
Basicaly my files structure is like this:
|---app
|---styles
|---***
*.css
*.html
.custom.json
I'm not sure if I really need a file similar to bower.json, but I made may own custom.json
{
"name": "custom",
"version": "0.0.1",
"main": [
"app/styles/**/*.css",
"app/scripts/**/*.js //pretend to inject custom js later
]
}
The task in gulpfile.js is like this:
gulp.task('myinject', function () {
var myinject = require('wiredep').stream;
var combined = Combine (
gulp.src('app/*.html'),
myinject({
directory: 'app',
bowerJson: require('./custom.json'),
dependencies: false,
html:
{
block: /(([ \t]*)<!--\s*custom:*(\S*)\s*-->)(\n|\r|.)*?(<!--\s*endcustom\s*-->)/gi,
detect: {
js: /<script.*src=['"](.+)['"]>/gi,
css: /<link.*href=['"](.+)['"]/gi
},
replace: {
js: '<script src="{{filePath}}"></script>',
css: '<link rel="stylesheet" href="{{filePath}}" />'
}
}
}),
gulp.dest('app')
);
combined.on('error', function(err) {
console.warn(err.message);
});
return combined;
});
Thanks in advance
I had a similar issue that I resolved with gulp-inject rather than wiredep.
Wiredep works fine with me when I need to include a third-party dependency (e.g. a bower package with a valid main file declared in the bower.json or a file with the same name as the directory).
However, when you want to include only a specific file (e.g. only the main.css of html5-boilerplate) gulp-inject is much simpler. Here is an extract of the doc :
gulp.task('index', function () {
var target = gulp.src('./src/index.html');
var sources = gulp.src(['./src/**/*.js', './src/**/*.css'], {read: false});
return target.pipe(inject(sources))
.pipe(gulp.dest('./src'));
});

Resources