How to override validation message of FOS bundle in symfony2 - php

I am using FOS bundle in my project. I created a new controller ChangePasswordController in my admin bundle. Here is my code.
namespace Pondip\AdminBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\Controller\ChangePasswordController as BaseController;
/**
Controller managing the change password *
#author Aman
/
class ChangePasswordController extends BaseController
{
/**
This action is used for change password for admin.
#author Aman
#return render view
#throws AccessDeniedException as exception
#
*/
public function changePasswordAction()
{
$user = $this->container->get('security.context')->getToken()->getUser();
$form = $this->container->get('fos_user.change_password.form');
$formHandler = $this->container->get('fos_user.change_password.form.handler');
$process = $formHandler->process($user);
if ($process) {
$this->setFlash('fos_user_success', 'Password has been changed successfully');
$url = $this->container->get('router')->generate('pondip_admin_changepassword');
return new RedirectResponse($url);
}
return $this->container->get('templating')->renderResponse(
'PondipAdminBundle:ChangePassword:changePassword.html.'.$this->container->getParameter('fos_user.template.engine'),
array('form' => $form->createView())
);
}
}
Now I want to customize error message for current password.

Are you overriding the FOSUserBundle? See Documentation. If you're not, then you should be, if you want to modify default behaviors.
If so, you then have to copy, in your bundle that is overriding the FOSUserBundle, the translation file (and parent directories) found in the FOSUserBundle vendor :
Resources/translations/validators.xx.yml where xx is your locale.
Then change the error messages. For example, to change the current password invalid error, write :
fos_user:
[...]
current_password:
invalid: Your own error message here
[...]
Hope this helps!

Related

Make a custom action in sonata admin bundle using CRUD controller

I want to make a custom page twig in Sonata admin bundle (clone for example ) :
I use this tutorial :
http://symfony.com/doc/current/bundles/SonataAdminBundle/cookbook/recipe_custom_action.html
this is my controller CRUDController.php:
<?php
// src/AppBundle/Controller/CRUDController.php
namespace AppBundle\Controller;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
class CRUDController extends Controller
{
// ...
/**
* #param $id
*/
public function cloneAction($id)
{
$object = $this->admin->getSubject();
if (!$object) {
throw new NotFoundHttpException(sprintf('unable to find the object with id : %s', $id));
}
// Be careful, you may need to overload the __clone method of your object
// to set its id to null !
$clonedObject = clone $object;
$clonedObject->setName($object->getName().' (Clone)');
$this->admin->create($clonedObject);
$this->addFlash('sonata_flash_success', 'Cloned successfully');
return new RedirectResponse($this->admin->generateUrl('list'));
// if you have a filtered list and want to keep your filters after the redirect
// return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters()));
}
}
but when i click in clone i show this error :
can you help me ..?
I feel like you forgot to configure your admin service for this page in the right way, please check http://symfony.com/doc/current/bundles/SonataAdminBundle/cookbook/recipe_custom_action.html#register-the-admin-as-a-service
cause sonata uses the SonataAdmin:CRUD controller by default and you should specify a custom one if you'd like to override the controller.
#src/AppBundle/Resources/config/admin.yml
services:
app.admin.car:
class: AppBundle\Admin\CarAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: Demo, label: Car }
arguments:
- null
- AppBundle\Entity\Car
- AppBundle:CRUD #put it here
You forget to configure Route for your controller.
Sonata Admin has to know about your new Action in order to generate for it route. For this purposes you have to configure configureRoutes method in you admin class:
class CarAdmin extends AbstractAdmin // For Symfony version > 3.1
{
// ...
/**
* #param RouteCollection $collection
*/
protected function configureRoutes(RouteCollection $collection)
{
$collection->add('clone', $this->getRouterIdParameter().'/clone');
}
}
As you can see the name of the route matches the name of the action (but without action!) in your CRUDController.
You had name of the action : 'cloneAction' so the name of the route is "clone".
In my case i forgot to add baseControllerName in app/config/admin.yml, before it was '~'

Disable automatic login after registration in Symfony's FOSUserbundle

Using latest Symfony and FOSUserbundle, after successfully registering a new user, the user is automatically logged in. I want to prevent this. My reason is that only a special user should be able to register new users.
I guess I have to override the registerAction in the RegisterController of the bundle, but I don't know how.
I tried: http://symfony.com/doc/current/bundles/FOSUserBundle/overriding_controllers.html, but it seems to be outdated, no user is created with this method.
Any hints are appreciated.
Edit:
I found out that I did not create the child bundle correctly. I also had to create my own EventListener. It works now when I overwrite the FOSUserEvents::REGISTRATION_SUCCESS event.
Strange thing is that when I use the FOSUserEvents::REGISTRATION_COMPLETEDevent, both events are dispatched, my bundle's and the FOSUserbundle's, so that the user is redirected to the correct site, but logged in as the new user.
Edit 2:
So this is in my listener:
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
FOSUserEvents::REGISTRATION_COMPLETED => 'onRegistrationCompleted',
);
}
public function onRegistrationSuccess(FormEvent $event)
{
$url = $this->router->generate('admin');
$event->setResponse(new RedirectResponse($url));
}
public function onRegistrationCompleted(FilterUserResponseEvent $event)
{
}
I set the redirection in the REGISTRATION_SUCCESSevent and the REGISTRATION_COMPLETEDis empty. With the debugger I can verify that my own listener's event is called, but the original event is also called.
Actually, there is no need to do any of these. The fos_user.listener.authentication service is removed from the container if use_authentication_listener is set to false.
See line 74-76 in FOS\UserBundle\DependencyInjection\FOSUserExtension.
This information is also included in document FOS UserBundle Configuration.
You can solve this problem with a Listener, In fos user bundle, it authenticates user with after registration.
file :friendsofsymfony/user-bundle/EventListener/AuthenticationListener.php
class : FOS\UserBundle\EventListener\AuthenticationListener
If you check this class you would see it tracks REGISTRATION_COMPLETED Event.
In Authenticatiton Listener It dispatches Event after triggering logInUser function. Therefore you have to logout user in your listener which subscribes `REGISTRATION COMPLETED.
you can check https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/controller_events.rst for writing your listener to logout user.
Note : It may not be a good way log-in log-out user in every registration process, but if you use fosuserbundle easiest way and minimum footprint would be this, if there is already a yml configuration doesn't exists, actually in code there is no direction of yml conf. So this approach would be min. footprint.
try {
$this->loginManager->logInUser($this->firewallName, $event->getUser(), $event->getResponse());
$eventDispatcher->dispatch(FOSUserEvents::SECURITY_IMPLICIT_LOGIN, new UserEvent($event->getUser(), $event->getRequest()));
} catch (AccountStatusException $ex) {
// We simply do not authenticate users which do not pass the user
// checker (not enabled, expired, etc.).
}
EDIT: This technique works on Symfony 3.3, I'm unaware if this works on lower versions.
The correct way of doing this is by creating a Compiler Pass.
You can also: Override the service by adding a new service using the same name: fos_user.listener.authentication on your app/config.yml file or on your bundle config file and adding your new class to it as I've done below and add this
Here is how to override the automatic logging when registering a new user using the compiler pass technique.
The Compiler Pass
namespace arpa3\UserBundle\DependencyInjection;
use arpa3\UserBundle\EventListener\AuthenticationListener;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class OverrideServiceCompilerPass implements CompilerPassInterface {
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('fos_user.listener.authentication');
$definition->setClass(AuthenticationListener::class);
}
}
The Service Override
namespace arpa3\UserBundle\EventListener;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Security\LoginManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
class AuthenticationListener implements EventSubscriberInterface
{
/**
* #var LoginManagerInterface
*/
private $loginManager;
/**
* #var string
*/
private $firewallName;
/**
* AuthenticationListener constructor.
*
* #param LoginManagerInterface $loginManager
* #param string $firewallName
*/
public function __construct(LoginManagerInterface $loginManager, $firewallName)
{
$this->loginManager = $loginManager;
$this->firewallName = $firewallName;
}
/**
* {#inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
// You can disable any of them or all of them as you want
//FOSUserEvents::REGISTRATION_COMPLETED => 'authenticate',
//FOSUserEvents::REGISTRATION_CONFIRMED => 'authenticate',
//FOSUserEvents::RESETTING_RESET_COMPLETED => 'authenticate',
);
}
/**
* #param FilterUserResponseEvent $event
* #param string $eventName
* #param EventDispatcherInterface $eventDispatcher
*/
public function authenticate(FilterUserResponseEvent $event, $eventName, EventDispatcherInterface $eventDispatcher)
{
try {
$this->loginManager->logInUser($this->firewallName, $event->getUser(), $event->getResponse());
$eventDispatcher->dispatch(FOSUserEvents::SECURITY_IMPLICIT_LOGIN, new UserEvent($event->getUser(), $event->getRequest()));
} catch (AccountStatusException $ex) {
// We simply do not authenticate users which do not pass the user
// checker (not enabled, expired, etc.).
}
}
}
Register your Compiler Pass on your main bundle file
namespace arpa3\UserBundle;
use arpa3\UserBundle\DependencyInjection\OverrideServiceCompilerPass;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class arpa3UserBundle extends Bundle {
public function getParent () {
return 'FOSUserBundle';
}
/**
*
* This injects a Compiler Pass that is used to override the automatic login after registration of a new user
* We have done this in order to disable the "by default" behaviour given that only admins can register users
* and logging in into the newly created account automatically is just not a desired behaviour
*
* #param ContainerBuilder $container
*/
public function build ( ContainerBuilder $container ) {
parent ::build( $container );
$container -> addCompilerPass( new OverrideServiceCompilerPass() );
}
}
There are other ways such as overriding the authentication service on your config.yml but the solution above is the cleanest and most maintainable solution I have found.
You are almost there, as you said your listeners are called but the order is not correct, so you need to make your listener be executed before the default one
In order to do that change
FOSUserEvents::REGISTRATION_SUCCESS =>
'onRegistrationSuccess'
to
FOSUserEvents::REGISTRATION_SUCCESS =>
['onRegistrationSuccess',-10],
Notice the -10 there, this changes the priority of the listener.
class RegistrationSuccessEventListener implements EventSubscriberInterface{
private $router;
public function __construct(UrlGeneratorInterface $router){
$this->router = $router;
}
public static function getSubscribedEvents()
{
//this will be called before
return array(
FOSUserEvents::REGISTRATION_SUCCESS => ['onUserRegistrationSuccess', -30],
);
}
/**
* #param FormEvent $event
* When the user registration is completed redirect
* to the employee list page and avoid the automatic
* mail sending and user authentication that happends
*
*/
public function onUserRegistrationSuccess(FormEvent $event){
$url = $this->router->generate('employees_list');
$event->setResponse(new RedirectResponse($url));
}
}
I am using symfony 2.8 with the FOSBundle version
friendsofsymfony/user-bundle dev-master 1f97ccf Symfony FOSUserBundle
according to the output of composer info

Why is my code returning a non response error? I am new to symfony2 and trying to implement the blog example explained in the symfony book

This is the code in my controller. Now according to the symfony book, this should allow the URL /blog/my-blog-post to be loaded with the slug variable acquiring a value of my-blog-post. However on execution, my controller returns an error "The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?" Any idea on how to resolve this?
Controller code
<?php
namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class BlogController extends Controller
{
/**
* #Route("/blog/{slug}", name="blog_show")
*/
public function showAction($slug)
{
//...
}
}
As you do not have the #Template annotation, you should render a template in your action return:
// renders app/Resources/views/hello/greetings/index.html.twig
return $this->render('hello/greetings/index.html.twig', array(
'name' => $name
));
More here: http://symfony.com/doc/current/book/controller.html#rendering-templates

How to create custom Facade in Laravel 4

Looked up a few tutorials on facades and laravel 4... tried some... not liked the way they work.
For instance, they don't all provide a way of defining where to store the facade files and service providers... and i tried to step away from that and got my head bumped into a few walls until i decided to do this thread.
So: Let's say i have an app called Laracms (laravel cms).
I'd like to store everything i create - facades, service providers, etc in a folder under app named laracms.
So i'd have /app/laracms/facades, /app/laracms/serviceproviders and so on. I don't want to mix the facades with the database models, i want to keep things as separate as possible.
Let's take now, in my case, the Settings name for the facade (i want to implement a settings class to use in views and admin to set up misc. stuff).
Settings::get(), Settings::set() as methods.
Can anyone explain how to set facades up correctly? I don't know what i'm doing wrong and i need a fresh start.
Thanks,
Chris
Looking for a step by step with simple explanations of how and why.
First you need to go to app/config/app.php and in providers section add:
'Laracms\Providers\SettingsServiceProvider',
In the same file in aliases section you should add:
'Settings' => 'Laracms\Facades\Settings',
In your app/Laracms/Providers you should create file SettingsServiceProvider.php
<?php
namespace Laracms\Providers;
use Illuminate\Support\ServiceProvider;
class SettingsServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('settings', function()
{
return new \Laracms\Settings();
});
}
}
In your app/Laracms/Facades/ you should create file Settings.php:
<?php
namespace Laracms\Facades;
use Illuminate\Support\Facades\Facade;
class Settings extends Facade {
protected static function getFacadeAccessor() { return 'settings'; }
}
Now in your app/Laracms directory you should create file Settings.php:
<?php
namespace Laracms;
class Settings {
public function get() {echo "get"; }
public function set() {echo "set"; }
}
As you wanted to have your files in custom folder Laracms you need to add this folder to your composer.json (If you used standard app/models folder you wouldn't need to add anything to this file). So now open composer.json file and in section autoload -> classmap you should add app/Laracms so this section of composer.json could look like this:
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php",
"app/Laracms"
]
},
Now you need to run in your console inside your project foler:
composer dump-autoload
to create class map
If everything is fine, you should now be able to use in your applications Settings::get() and Settings:set()
You need to notice that I used folders with uppercases because namespaces by convention starts with upper letters.
There are three components to making a Facade:
The wanna be Facade Class, that class that needs to become a facade.
The Facade required Class, which tells Laravel which registered class it pertains to
A Service Provider, which registers the Facade class in the App container
1. the wanna be Facade Class:
<?php namespace Moubarmij\Services\ModelsServices;
class AuthenticationService extends MoubarmijService implements AuthenticationServiceInterface{
/**
* #param $email
* #param $password
*
* #return mixed
*/
public function login($email, $password)
{
return Sentry::authenticate([
'email' => $email,
'password' => $password,
]);
}
/**
* #return mixed
*/
public function logout()
{
return Sentry::logout();
}
}
2. the required class for the facade to work:
<?php namespace Moubarmij\Facades;
use Illuminate\Support\Facades\Facade;
/**
* Class AuthenticationServiceFacade
* #package Moubarmij\Services\ModelsServices
*/
class AuthenticationServiceFacade extends Facade{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'authentication_service'; }
}
note: authentication_service can be anything you want (its the name of the component registered in the IOC)
3. the service provider
<?php namespace Moubarmij\Providers;
use Illuminate\Support\ServiceProvider;
/**
* A service provider for the Authentication Service
*
* Class AuthenticationServiceSP
* #package Moubarmij\Providers
*/
class AuthenticationServiceSP extends ServiceProvider {
/**
* bind interfaces
*
* #return void
*/
public function register()
{
// Register 'authentication_service' instance container to our AuthenticationService object
$this->app['authentication_service'] = $this->app->share(function($app)
{
return $app->make('Moubarmij\Services\ModelsServices\AuthenticationService');
});
// Shortcut to auto add the Alias in app/config/app.php
$this->app->booting(function()
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('AuthenticationService', 'Moubarmij\Facades\AuthenticationServiceFacade');
});
}
}

Symfony2 : Repository Class not found

Today I stuck in Repository Class Function I got this error
Undefined method 'test'. The method name must start with either findBy or findOneBy!
I allready checked these solutions -
Solution 1
Solution 2
Solution 3
Is anything I need to add into config file ?
This is my Entity Class
// src/Foo/NewsBundle/Entity/News.php
namespace Foo\NewsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* News
* #ORM\Entity(repositoryClass="Foo\NewsBundle\Repository\NewsRepository")
* #ORM\Table(name="news")
* #ORM\HasLifecycleCallbacks()
*/
class News
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $title;
This is my repository Class
// Foo/NewsBundle/Repository/NewsRepository.php
namespace Foo\NewsBundle\Repository;
use Doctrine\ORM\EntityRepository;
Class NewsRepository extends EntityRepository
{
public function test()
{
return "Nisarg";
}
}
And I am calling this test() function this wat from the controller
public function indexAction()
{
// $news = $this->getDoctrine()
// ->getRepository('FooNewsBundle:News')
// ->findAll();
$em = $this->getDoctrine()
->getManager();
$news = $em->getRepository('FooNewsBundle:News')->test();
if (!$news) {
throw $this->createNotFoundException('No news found');
}
$build['news'] = $news;
return $this->render('FooNewsBundle:Default:news_show_all.html.twig', $build);
}
Check if you have specified your repository class in your News orm config file.
There must be somthing like "repositoryClass: Foo\NewsBundle\Repository\NewsRepository"
And don't forget to clear cache!
In your entity you are not using annotation, check if you have a news.yml file in Resources/config/doctrine
I think the standard for repository classes is to put it in a subdirectory of the entity folder and still use the same entity namespace. Yours used a different namespace which is why I think you have the error.
According to the cookbook this is how the entity and custom respistory are defined.
link to custom repository class in the cookbook.
Entity
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Acme\StoreBundle\Entity\ProductRepository")
*/
class Product
{
//...
}
Repository:
// src/Acme/StoreBundle/Entity/ProductRepository.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
}
Fedor Petrick is right!
You should look for the orm file that corresponds to the entity.
In my case, I have created a custom repository named: OfertaRepository.php in the folder OfertaBundle\Entity
On the other hand, I have a file Oferta.orm.xml
In line three , It said :
<entity name="OfertaBundle\Entity\Oferta" table="oferta">
But it should be :
<entity name="OfertaBundle\Entity\Oferta" table="oferta" repository-class="OfertaBundle\Entity\OfertaRepository">
Now, the method in the OfertaRepository.php works well!
Your code look correct in the Entity and Repository. Perhaps you could try to call the getRepository directly without ->getManager.
$this->getDoctrine->getRepository('FooNewsBundle:News')->test();
If you need a specific field you should have a look at the short notations with findOneBy and findBy in most cases its much easier instead of writing a custom class.
http://symfony.com/doc/current/book/doctrine.html
Are you in production ? Perhaps clearing your cache is the solution :
php app/console cache:clear --env=prod --no-debug
If clearing your cache doesn't work, is $em->getRepository('FooNewsBundle:News') instanceOf Foo\NewsBundle\Repository\NewsRepository true or false? By the looks of things, your not getting the correct repository somehow?
have you generated the entities?
php app/console doctrine:generate:entities BUNDLENAME
launch this command and then retry your code

Categories