How to generate symfony2 translations inside controller? - symfony

Symfony2 project. I'm using JMSTranslationsBundle.
Here is a fragment from function inside controller:
if ($user->isAccountConfirmed()) {
$this->toolbar->addInfo('user.account.confirmed');
}
How to generate translation for 'user.account.confirmed' in .xliff file? I mean, what code I should add to this function to be able to translate it?

Looking at the available extraction methods, it explains that there is no automatic extraction for your case available.
You will need to use trans (or any of the other methods explained) in your template or in the controller. Without this hint, the extractor will not be able to find your message. Personally I have used TranslationContainerInterface in one my projects.
With that you simply define a new method in your controller, which returns the "to-be-translated" strings:
<?php
// ...
use JMS\TranslationBundle\Translation\TranslationContainerInterface;
use JMS\TranslationBundle\Model\Message;
class AcmeController extends Controller implements TranslationContainerInterface
{
/**
* {#inheritdoc}
*/
static function getTranslationMessages()
{
return [
Message::create('user.account.confirmed')
];
}
}
An alternate solution would be to directly use the translater service. The call to this service should then again be visible to the extractor. E.g:
/** #var $translator \Symfony\Component\Translation\TranslatorInterface */
$translator = $this->get('translator');
if ($user->isAccountConfirmed()) {
$this->toolbar->addInfo(
$translator->trans('user.account.confirmed')
);
}

Related

Symfony2-entityManager inside an Entity

I am using Symfony version 2.7.6. I have created an entity named EmployeeBasicInfo having fields
firstname
lastname
identificationCode etc
I have created a callback function for validating Identification code in EmployeeBasicInfo entity itself which looks like
/**
* #Assert\Callback(groups={"edit_myinfo"})
*/
public function validateIdentificationCode(ExecutionContextInterface $context)
{
if ($this->getEmployeeFirstName() == 'fakename') {
$context->buildViolation('This name sounds totally fake!')
->atPath('employeeFirstName')
->addViolation();
}
}
and this callback function works properly
Actually I want such a callback functionality which checks identidfication code against database. I have added $em = $this->getDoctrine()->getManager(); inside the callback function and the error is like Attempted to call an undefined method named "getDoctrine" of class "XXX\EmployeeBundle\Entity\EmployeeBasicInfo".. Please advise me the effective way
Do not inject the EntityManager in your Entity. One basic concept of the DataMapper-Pattern is, that your entity does not have to know about your data source and its connectors.
I'd suggest to write a custom validation constraint, in which you inject the dependencies you need.
EntityManager, Repository to query, etc. Whatever service suits you.
Have a look at how to create custom constraint validators with dependencies
I would suggest you use a service to do this
class EmployeeUtility($connection)
{
public function __construct($conn) { $this->connection = $v; }
public function validateIdentificationCode($emloyeeId, $validationCode)
{
// Your code here
}
}
In your controller, you inject the service:
$employeeUtility = $this->get('employee.utility');
$employeeUtility->validateIdentificationCode(1,'GF38883dkDdW3373d');
Alternatively, add the code in a repository class.

Symfony: Where do I put the code to register a custom MimeTypeGuesser?

I need to register a custom MimeTypeGuesser so I can add in some logic to handle .docx files, which my web server's installation of PHP is treating as application/zip (which is technically correct). I want it recognized as application/msword or application/vnd.openxmlformats-officedocument.wordprocessingml.document.
In the filedoc comment for the class Symfony\Component\HttpFoundation\File\MimeType, there's some info on how to register a custom guesser:
* You can register custom guessers by calling the register() method on the
* singleton instance. Custom guessers are always called before any default ones.
*
* $guesser = MimeTypeGuesser::getInstance();
* $guesser->register(new MyCustomMimeTypeGuesser());
Great, but where is an appropriate place for this registration code?
I'd want this to be an app-wide change, but I can't think of a good place to put it.
To be honest, I myself can't say for sure which place would be appropriate to register custom guesser, so I will simply suggest you one.
Since you're aiming for a app-wide solution, I believe overriding the build method of your Bundle class would do the trick.
Let's assume your bundle is named AppBundle, then your Bundle configuration file should be AppBundle.php located in src/AppBundle/AppBundle.php
Normally that file should be nothing, but an empty class that extends Symfony's core Bundle class - Symfony\Component\HttpKernel\Bundle\Bundle.
From there you can override the build method that we inherit from Bundle\Bundle and register your guesser. Normally build() is used to register custom extension, such as payment gateways, or compiler passes and stuff like that. You can see that left in comments actually:
/**
* Builds the bundle.
*
* It is only ever called once when the cache is empty.
*
* This method can be overridden to register compilation passes,
* other extensions, ...
*
* #param ContainerBuilder $container A ContainerBuilder instance
*/
public function build(ContainerBuilder $container)
{
}
So, add the following statements at the top of your class:
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
use Symfony\Component\DependencyInjection\ContainerBuilder;
And then simply override the method:
public function build(ContainerBuilder $container) {
parent::build($container);
$guesser = MimeTypeGuesser::getInstance();
$guesser->register( new MyMimeTypeGuesser() );
}
This would load your custom guesser once the application loads all of its bundles. I would like to say that this might not be the perfect solution, but for the time being it can help you.
You can do it in Bundle::boot:
use AppBundle\HttpFoundation\File\MimeType\MyCustomMimeTypeGuesser;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
/**
* {#inheritdoc}
*/
public function boot()
{
parent::boot();
MimeTypeGuesser::getInstance()->register(new MyCustomMimeTypeGuesser());
}
...
}
This will be called every time the Kernel boots.
If you do it in Bundle::build it would not work, because the call needs to happen at runtime.

Can I implement my own Symfony2 annotations easily?

Is there anything in the Symfony annotations modules that allow me to use them for other uses?
I know for #Route and #Method you need to extend existing libraries, so its just not that easy i'm guessing.
Currently, i'm working with the JS History API, and would LOVE to put the popState data for my JS files in the annotations. So they are already available when the routing generates the URL.
Q Doesn't this makes sense to have a, HTML5 annotated title, or some attribute here? It would be great to have the ability to define this data, as annotated, right next to the already existing route name and stuff.
Q Is there anybody that has tweaked with the annotations before?
I wanted to clarify my intentions here as I think I left out some crucial details (the mention of History API) for understanding my use case.
There is a few SPA front ends that have been integrated through a front-end bundle, and this connected via AJAX calls to a backend bundle which was a straight RESTful API, with the addition of a very fun-to-develop PHP API class I made that intereprets and processes (routes) the AJAX in a fashion that directly executes other PHP class controller `methods.
I use a lot of ajax for this Symfony 2 app (fosjsrouter) to handle routing. So instead of URLs triggering the routes and actions, the SPA click event fires off AJAX to the back end router, with a large JSON payload, not limited to PHP control parameter's (class/method/var names), and data sets.
OK, so getting back on track; Given the above scenario; In the JS class object end of the router, inside this I thought it was the best place to add some JS History API functionality to it, (state, back button, etc.)
The above class can be called if a history flag was called, which could become responsible for assigning initial state data. Primarily, this is because the JSON data object that's being around in this JS method contains already a lot of the crucial route data, and param information for that route needed in the backend PHP, which comes from the annotations.
So the idea is if I add accessibility for a history state title and URL to the annotations, then I will have access to that information right there available to define the initial state, if flagged, right inside the an ajax.done(), inside this main JS routing method.
Now we can pass state back and forth two ways between the db and realtime client-side async. You can use an observer, or anything, from there front-end, and jobs/queues on the backend to keep it fully reactive. (use React too :-))
EDIT I'm not so sure that's what I was thinking, it looks like its making me set the values of the title and url for this inside the return statement of the PHP function, where I want it set in the annotation (see return 'Matthias Noback';)
So I'm trying this, but where do I set these titles at?
<?php
namespace Blah\CoreBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* #Annotation
*/
class HistoryAnnotationController
{
//history state params are out properties here..
/**
* #var
*/
private $url;
/**
* #var
*/
private $title;
/**
*
*/
public function __construct()
{
}
/**
* #return mixed
*/
public function getTitle()
{
return $this->title;
}
/**
* #return mixed
*/
public function getUrl()
{
return $this->url;
}
}
I want to set it WAY back here, so the ajax that calls this route has access to it.. (look for #historyApiTitle in this code, etc..)
<?php
namespace Blah\Bundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller,
Symfony\Component\HttpFoundation\JsonResponse,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Method,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Route,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Template,
Blah\Bundle\Entity\Test,
Doctrine\ORM\Query; //for hydration
class StuffController
{
/**
* #Route("/some/route/name/{test}", name="some_route_name", options={"expose"=true})
* #param $test
* #return mixed
* #historyApiTitle('This is the get something page')
* #historyApiUrl('/get_something')
*/
public function getSomethingAction($test)
{
$em = $this->getDoctrine()->getManager();
$dql = "
SELECT s
FROM BlahBundle:Stuff s
WHERE s.test = :test";
$query = $em->createQuery($dql);
$query->setParameter('test', $test);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($query,
$this->get('request')->query->get('page', 1), 1000);
return $this->render('BlahBundle:Stuff:get_something.html.twig', array('pagination' => $pagination));
}
}
Q So looking at these TWO code examples, how do I connect the dots between the two to get this to work?
Yes you can annotations classes you can follow the following tutorial Creating Custom annotations Classes
Basic rules are the follows:
Your class should have the #Annotation -phpdoc comment
/**
* #Annotation
*/
class CustomAnnotation
{
public function __construct($options) {}
}
In Your Needed class just use it in standard way;
class Person
{
/**
* #CustomAnnotation("option")
*/
public function getName()
{
return 'some stuff';
}
}
You should looks at the AOPBundle, it allows you to do treatement from your personnals annotations. But I don't thinks trying to do annotations in the view is a good idea. You need to parse the javascript with php, and it sounds bad.

How do I create a custom exclusion strategy for JMS Serializer that allows me to make run-time decisions about whether to include a particular field?

As the title says, I am trying to make a run-time decision on whether or not to include fields in the serialization. In my case, this decision will be based on permissions.
I am using Symfony 2, so what I'm looking to do is add an additional annotation called #ExcludeIf which accepts a security expression.
I can handle the annotation parsing and storing of the meta data, but I am not able to see how to integrate a custom exclusion strategy with the library.
Any suggestions?
Note: exclusion strategies are an actual construct in the JMS codebase, I just haven't been able to figure out the best way to integrate an extra on top of the others
PS: I had asked about this before and was pointed to using groups. For various reasons this is a very poor solution for my needs.
You just have to create a class that implements JMS\Serializer\Exclusion\ExclusionStrategyInterface
<?php
namespace JMS\Serializer\Exclusion;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Context;
interface ExclusionStrategyInterface
{
/**
* Whether the class should be skipped.
*
* #param ClassMetadata $metadata
*
* #return boolean
*/
public function shouldSkipClass(ClassMetadata $metadata, Context $context);
/**
* Whether the property should be skipped.
*
* #param PropertyMetadata $property
*
* #return boolean
*/
public function shouldSkipProperty(PropertyMetadata $property, Context $context);
}
In your case, you can implement your own custom logic in the shouldSkipProperty method and always return false for shouldSkipClass.
Example of implementation can be found in the JMS/Serializer repository
We will reference the created service as acme.my_exclusion_strategy_service below.
In your controller action:
<?php
use Symfony\Component\HttpFoundation\Response;
use JMS\Serializer\SerializationContext;
// ....
$context = SerializationContext::create()
->addExclusionStrategy($this->get('acme.my_exclusion_strategy_service'));
$serial = $this->get('jms_serializer')->serialize($object, 'json', $context);
return new Response($serial, Response::HTTP_OK, array('Content-Type' => 'application/json'));
Or if you are using FOSRestBundle
<?php
use FOS\RestBundle\View;
use JMS\Serializer\SerializationContext;
// ....
$context = SerializationContext::create()
->addExclusionStrategy($this->get('acme.my_exclusion_strategy_service'))
$view = new View($object);
$view->setSerializationContext($context);
// or you can create your own view factory that handles the creation
// of the context for you
return $this->get('fos_rest.view_handler')->handle($view);
As of jms/serializer 1.4.0, the symfony expression language is integrated in its core.
The feature is documented at http://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies#dynamic-exclusion-strategy and this allows to use runtime exclusion strategies.
An example taken from the documentation is:
class MyObject
{
/**
* #Exclude(if="service('user_manager_service').getSomeRuntimeData(object)")
*/
private $name;
/**
* #Expose(if="service('request_stack').getCurrent().has('foo')")
*/
private $name2;
}
I this example, the services user_manager_service and request_stack are invoked at runtime, and depending on the return (true or false), the property will be exposed or not.
With the same expression language, as of 1.6.0 is possible also to use virtual properties via expression language.
Documented at http://jmsyst.com/libs/serializer/master/reference/annotations#virtualproperty allows to add on the fly data coming from external services

Symfony Newb Routing Issue

I have just started using Symfony and I am having a routing problem. Here is the routing fromt the controller:
/**
* #Route("/social/{name}/", name="_speed1")
* #Route("/social/drivers/")
* #Route("/social/drivers/{name}/", name="_driver")
* #Route("/social/", name="_speed")
* #Template()
*/
public function unlimitedAction()
{
If I go to speed/social/ or speed/social/bob or speed/social/drivers/ or speed/social/drivers/bob all of those pages render with no problem. However I need the name being passed in so I changed
public function unlimitedAction()
{
to
public function unlimitedAction($name)
{
If I go to speed/social/drivers/ or speed/social/drivers/bob it returns fine. However, if I go to speed/social/ then I get the following error:
Controller "MyBundle\Controller\DefaultController::unlimitedAction()"
requires that you provide a value for the "$name" argument (because there is
no default value or because there is a non optional argument after this one).
I can't understand why it works for one route but not the other.
So my question is, how can I acheive my routing so that I can go to:
speed/social/
speed/social/drivers/
speed/social/drivers/bob
And be able to pass the variable to the action without error.
Thanks!
To answer your question: you have to provide a default value for name parameter, for each route without the {name} parameter in the url. I can't test it right now and I can't remember the syntax when using annotations, but should be something like this:
/**
* #Route("/social/{name}/", name="_speed1", defaults={"name"=null})
* #Route("/social/drivers/{name}/", name="_driver", defaults={"name"=null})
* #Template()
*/
public function unlimitedAction($name)
{
}
This way you should be able to call /social/ and /social/foo as well as /social/drivers/ and /social/drivers/foo.
But, really, this is not the right way to go. Just define more actions, each binded to a single route:
/**
* #Route("/social", name="social_index")
* #Template()
*/
public function socialIndexAction() { } // /social
/**
* #Route("/social/{name}", name="social_show")
* #Template()
*/
public function socialShowAction($name) { } // /social/foo
As a general rule, each method (each action) should be focused to do just one thing and should be as short as possible. Use services and make your controllers do what they are supposed to do: understand user input, call services and show views.

Resources