Sonata Admin custom action seems not using the custom controller - symfony

I'm trying to make a custom action but I'm gettin this error:
Controller "Sonata\AdminBundle\Controller\CRUDController::editarDistribucionAction" for URI "/admin/test/tarifas/distribucionperiodos/6/distribucion/editar" is not callable.
I have reviewed the code many times but I can't find the mistake:
This is my custom controller:
class CustomActionsController extends CRUDController
{
public function editarDistribucionAction(){
//$id = $request->get($this->admin->getIdParameter());
//TODO:
}
}
And this is my services.yml
sonata.admin.editarDistribucion:
class: Test\TarifasBundle\Admin\DistribucionPeriodosTablaAdmin
tags:
- name: sonata.admin
manager_type: orm
group: "Tarifas"
label: "DistribuciĆ³n de periodos"
arguments: [ null, Test\TarifasBundle\Entity\DistribucionPeriodosTabla, TarifasBundle:CustomActions ]
I have tried to put wrong parameters on services.yml expecting a different error, but i get exacly the same, so it seems that is ignoring this piece of services.yml
Thanks in advance!!

It was a concept mistake. I followed the official doc step by step but y was mixing different admin clases. The configureRoutes code was in one class and I was trying to edit another one.

Related

Routing changes when upgrading easyadmin bundle symfony

I am in charge of upgrading easyadmin bundle on an app that was previously built using symfony v4.4.19. Initially we had: easycorp/easyadmin-bundle v2.3.12. Then, we decided to upgrade the easyadmin bundle to v3 because we faced some issues when enabling/disabling a boolean property from the list view.
When I was using the v2 :
php bin/console debug:router showed a route called easyadmin with a path /myworkshop/ .
I had no Dashboard controller nor NecklaceCrudController, I simply had a controlller called
AccessoriesController.php with several actions like deleteAction that is executed when the user
deletes an entity, editAction when the user edits an entity, SearchAction ...
In the deleteAction there is this line of code:
return $this->redirect($this->generateUrl('easyadmin', array('action' => 'list', 'entity'=> $this->entity['name'])));
so the url would become something like this
/myworkshop/?action=list&entity=necklace
To open the easy admin interface I have to click on a menu link whose link is :
->createItem('Visit my workshop', ['route' => 'easyadmin']);
When I open this interface /references, I get the list of the different entities in my app, if
I select one, I see the list view and I can edit one entity successfully but I cannot
enable/disable boolean properties from the list view as mentioned earlier.
We specified /myworkshop instead of /admin in app>config>routing.yml
# easy admin
easy_admin_bundle:
resource: "#myShop/Controller/AccessoriesController.php"
type: annotation
prefix: /myworkshop```
- A custom css was successfully employed in : app>config>config.yml
easy_admin:
design:
assets:
css:
- 'bundles/css/easyadmin.css
When I upgraded to v3:
php bin/console debug:router showed a route called myshop_admin_dashboard_index (which was
automatically generated) with a path /easyadmin.
Dashboard controller and NecklaceCrudController were created, The DashboardController only has configureCrud() and configureMenuItems() functions. The latter contains the links yield MenuItem::linkToCrud . Question 1 : In version 2 no menu links where created explicitely like here, so I was wondering how was the complete list of entities correctly showing up on my application interface?
In DashboardController there is no index () function nor a route nor a link just configureCrud() and configureMenuItems() functions.
I want to keep the AccessoriesController.php with his several actions but now, with the new route and path, it is completely being ignored. Question 2 : Is there something that I have to change in the generateUrl part? can someone give me an example of what this will become if I opt for adminUrlgenerator like I read in the documentation?
The routing.yml file remains the same however, my easyadmin interface appears only when visiting this link /easyadmin instead of /myworkshop . Question 3: I want to keep the /workshop url , what should I do in addition to keeping the routing.yml as it is now?
I wish we could change myshop_admin_dashboard_index to easyadmin and /easyadmin to my /myworkshop as it was in version 2, because there are many parts in AccessoriesController where I use $this->generateUrl('easyadmin',
Question 4: The css is no longer applicable any idea why? could be related to the AccessoriesController that is not currently being taken into account.
Well, you can add the index method to your dashboard controller with route annotation to change the route
class DashboardController extends AbstractDashboardController
{
/**
* #Route("/myworkshop", name="admin")
*/
public function index(): Response
{
return $this->render('dashboard/index.html.twig');
}
You can add any route to easyadmin menu like this
class DashboardController extends AbstractDashboardController
{
public function configureMenuItems(): iterable
{
yield MenuItem::linktoRoute('Some Route', 'fa fa-info', 'route_name_here');
#...
}
}
You can add any CSS/js file too
class DashboardController extends AbstractDashboardController
{
public function configureAssets(): Assets
{
return Assets::new()
->addCssFile('build/admin.css')
->addJsFile('build/admin.js')
;
}
}

Overriding Registration Form (SonataUserBundle) since Symfony2 Update to 2.8

After updating symfony2 with the dependencies to 2.8 I get the following error message when trying to override the sonata user bundle registration form:
The field type "Sonata\UserBundle\Form\Type\RegistrationFormType" is not registered with the service container.
If I switch back to Symfony 2.7 everything works again.
My services.yml:
sonata.user.registration.form.type:
class: My\Bundle\Form\Type\RegistrationFormType
arguments: [ "%fos_user.model.user.class%" , "#service_container"]
tags:
- { name: form.type, alias: sonata_user_registration }
In my controller the following line triggers the error:
$form = $this->container->get( 'sonata.user.registration.form' );
Unfortunately I couldn't find any resources on this subject (i.e. if there are any changes in overriding the registration form since the latest version)
Ok, this isn't a bug, but a new feature. You have to use the build() and boot() methods in your bundle to register your FormType via FormHelper::registerFormTypeMapping.

Multiple Twig_Extension in Symfony2

I want to register my custom Twig filters in separate bundle (to avoid having one huge file).
I have Yaml configurations in each bundle:
# services.yml
services:
twig.extension.[BundleName]:
class: Kuba\[BundleName]\Twig\AppExtension
public: false
tags:
- { name: twig.extension }
Yet, all the time the last (lexicographically) extension overrides previous one.
How can I register more than one extension and if it's not possible what would be de best practise to split the code?
Twig extensions are identified by the string returned by getName() method. Is there a reason you have to return same string in all your twig extension!

Symfony2 multi-level dynamic router

I have a current project that has to displays both defined pages with specific entities, what is very easy to manage with Symfony2, and content pages on different layouts, what is - I guess - a bit less common.
I get in trouble trying to build the routing system.
For instance, if I have to display a page with some news,
I would like to update the router of my bundle with a new route like :
my_bundle_news_page:
pattern: /news
defaults:
_controller: MyBundle:NewsController:indexAction
But how to manage a dynamic router that could have a totally custom URL on many levels ?
Let's imagine I've got a "Page" Entity, that is self-references for an optionnal "parent-child" relation.
I don't think I can just use any config YAML file for this specific routing ?!
my_bundle_custom_page:
pattern: /{slug}
defaults:
_controller: MyBundle:PageController:showAction
This would bind all the first-level pages:
/projects
/about
/contact
/our-project
What about a page that would be displayed with, for instance, a slug like:
/our-project/health
In fact any URL...
/{slug-level1}/{slug-level2}/{slug-level3} etc.
Cause the pages are supposed to change and be updated from webmastering.
I guess the best way would be to have a router that compare the {slug} with a database field (entity property)
I read in the Symfony-CMF doc that it is possible to write a service based a route provider:
namespace MyBundle\Routing;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route as SymfonyRoute;
use MyBundle\Entity\PageRepository;
class RouteProvider extends PageRepository {
public function findPageBySlug($slug)
{
// Find a page by slug property
$page = $this->findOneBySlug($slug);
if (!$page) {
// Maybe any custom Exception
throw $this->createNotFoundException('The page you are looking for does not exists.');
}
$pattern = $page->getUrl(); // e.g. "/first-level/second-level/third-level"
$collection = new RouteCollection();
// create a new Route and set our page as a default
// (so that we can retrieve it from the request)
$route = new SymfonyRoute($pattern, array(
'page' => $page,
));
// add the route to the RouteCollection using a unique ID as the key.
$collection->add('page_'.uniqid(), $route);
return $collection;
}
}
But how to set it up as a service ? Are there some requirements ?
How could this kind of thing work, does it add a route to the RouteCollection when request is called ?
And will I be able to bind any route in this way ?
EDIT : services.yml of my bundle
parameters:
cmf_routing.matcher.dummy_collection.class: Symfony\Component\Routing\RouteCollection
cmf_routing.matcher.dummy_context.class: Symfony\Component\Routing\RequestContext
cmf_routing.generator.class: Symfony\Cmf\Bundle\RoutingBundle\Routing\ContentAwareGenerator
cmf_routing.nested_matcher.class: Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
cmf_routing.url_matcher.class: Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
fsbcms.chain_router.class: Symfony\Cmf\Component\Routing\ChainRouter
fsbcms.route_provider.class: FSB\CMSBundle\Routing\RouteProvider
fsbcms.dynamic_router.class: Symfony\Cmf\Component\Routing\DynamicRouter
fsbcms.route_entity.class: null
services:
fsbcms.router:
class: %fsbcms.chain_router.class%
arguments:
- "#logger"
calls:
- [setContext, ["router.request_context"]]
fsbcms.route_provider:
class: "%fsbcms.route_provider.class%"
arguments:
- "#doctrine"
cmf_routing.matcher.dummy_collection:
class: "%cmf_routing.matcher.dummy_collection.class%"
public: "false"
cmf_routing.matcher.dummy_context:
class: "%cmf_routing.matcher.dummy_context.class%"
public: false
cmf_routing.generator:
class: "%cmf_routing.generator.class%"
arguments:
- "#fsbcms.route_provider"
- "#logger"
calls:
- [setContainer, ["service_container"]]
- [setContentRepository, ["cmf_routing.content_repository"]]
cmf_routing.url_matcher:
class: "%cmf_routing.url_matcher.class%"
arguments: ["#cmf_routing.matcher.dummy_collection", "#cmf_routing.matcher.dummy_context"]
cmf_routing.nested_matcher:
class: "%cmf_routing.nested_matcher.class%"
arguments: ["#fsbcms.route_provider"]
calls:
- [setFinalMatcher, ["cmf_routing.url_matcher"]]
fsbcms.dynamic_router:
class: "%fsbcms.dynamic_router.class%"
arguments:
- "#router.request_context"
- "#cmf_routing.nested_matcher"
- "#cmf_routing.generator"
tags:
- { name: router, priority: 300 }
I suggest taking a look at the Symfony CMF routing component and the CmfRoutingBundle (to implement the component in symfony).
The Routing component uses a chain router, which is irrelevant for this question but it's good to know. The chain router chains over a queue of routers. The component provides a DynamicRouter that uses a NestedMatcher. That's exactly what you want.
The NestedMatcher uses a Route provider to get the routes from a dynamic source (e.g. a database). You are showing an example of a Route provider in your question.
Furthermore, it uses a FinalMatcher to match the route. You can just pass an instance of Symfony\Cmf\Component\Routing\NestedMatcher\UrlMatcher, as you are doing not too difficult things.
Take a look at the docs of the RoutingBundle to learn how to activate the chain router and then create a route provider which loads the routes, make a service:
acme_routing.route_provider:
class: Acme\RoutingBundle\Provider\DoctrineOrmProvider
arguments: ["#doctrine"]
Now, you can create a NestedMatcher service:
acme_routing.url_matcher:
class: Symfony\Cmf\Component\Routing\NestedMatcher\UrlMatcher
arguments: ["#cmf_routing.matcher.dummy_collection", "#cmf_routing.matcher.dummy_context"]
acme_routing.nested_matcher:
class: Symfony\Cmf\Component\Routing\NestedMatcher
arguments: ["#acme_routing.route_provider"]
calls:
- [setFinalMatcher, ["acme_routing.url_matcher"]]
Now, register the DynamicRouter and put it in the chain:
acme_routing.dynamic_router:
class: Symfony\Cmf\Component\Routing\DynamicRouter
arguments:
- "#router.request_context"
- "#acme_routing.nested_matcher"
- "#cmf_routing.generator"
tags:
- { name: router, priority: 300 }
Now, it should work and should load the routes from the database and match them against the request.

Sonata Admin configuration with FosUser but *not* SonataUser

As per the title I'm using Sonata Admin and FosUser.
Sonata Admin is working fine for my normal entities; I can do the usual CRUD things.
I then followed this example in the docs for hooking into FosUser
My services.yml is thus:
sonata.admin.user:
class: Company\MyBundle\Admin\UserAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Administration", label: "Users" }
arguments: [ null, Company\UserBundle\Entity\User, SonataAdminBundle:CRUD ]
calls:
- [ setUserManager, [ #fos_user.user_manager ] ]
... and I've created UserAdmin.php:
namespace Company\MyBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\AdminBundle\Form\FormMapper;
use FOS\UserBundle\Model\UserManagerInterface;
class UserAdmin extends Admin
{
I get a user link in the dashboard but when I follow create new or click to edit my fixtures user (BTW the rendered route is http://dev.url/app_dev.php/admin/company/user/user/create) I get a 500 server error saying "Class does not exist":
in /my/path/company/codebase/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php at line 199
$class = $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName;
}
$proxyClass = new \ReflectionClass($class);
if ($proxyClass->implementsInterface($this->proxyInterfaceName)) {
$class = $proxyClass->getParentClass()->getName();
}
I've fiddled with some of the service settings, the only things I can see different to the example are:
I've changed the namespace to point to my admin class folder
%fos.user.admin.user.entity% wasn't configured anywhere so I've hardcoded it to be Company\UserBundle\Entity\User
I did start to look at adding the SonataUser bundle but as I'm still finding my feet with Symfony2 I figured I'd keep things simple for now and get an unserstanding of what's going wrong here. given the example doesn't mention it as a dependency I assume I can run without it?

Resources