Accept null value when passing a non existing id parameter with #ParamConverter - symfony

Is it possible to return "null" value when passing an non existing "id" in my route ?
/**
* #Route("/admin/product/edit/{id}", name="product_edit", methods={"POST"})
* #ParamConverter(
* name="id",
* class="App\Entity\Product",
* options={"mapping": {"id": "id"}}
* )
* #param Product $product
* #param Request $request
* #return JsonResponse
*/
public function edit(Product $product = null, Request $request)
{
var_dump($product);die;
$user = $this->getUser();
if (!$product || $product->getUser() !== $user) {
throw new HttpException(500, "Product does not exist");
}
return new JsonResponse([]);
}
It always give me the error :
App\\Entity\\Product object not found by the #ParamConverter annotation.
I want to throw an HttpException when null value returned. I also tried to change "isOptionnal" parameter to "false" in ParamConverter annotation but it's not working.

Remove #ParamConverter and you can use "/admin/product/edit/{id?}"
See this

Related

Symfony onKernetRequest detect which table

I have the following Listener in my code:
class BeforeRequestListener
{
/**
* #var EntityManager
*/
private $em;
/**
* #var SessionInterface
*/
private $session;
/**
* BeforeRequestListener constructor.
* #param EntityManager $em
* #param SessionInterface $session
*/
public function __construct(EntityManager $em,SessionInterface $session)
{
$this->em = $em;
$this->session =$session;
}
/**
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event){
//if() HERE
$filter = $this->em->getFilters()
->enable(Utils::CLIENT_FILTER_NAME);
$filter->setParameter(Utils::CLIENT_ID_NAME, $this->session->get(Utils::CLIENT_ID_NAME));
}
}
And this listener is made to enable some filter. i need to detect on which table/tables this is request id done so i can disable it when there is no column called client_id which is btw the CLIENT_ID_NAME const.
the reason is mentioned here : Doctrine and symfony filter, debug the filter
i need to apply the filter only after a user logs in and some tables doesnt have the client_id field so i want to disable that check on these tables.
Thanks!
If your request contains some information about tables, you can retrieve it like this:
$tables = $event->getRequest()->get('tables');
or you can assume affected entity/table by requested route:
$request = $event->getRequest();
// Matched route
$_route = $request->attributes->get('_route');
// Matched controller
$_controller = $request->attributes->get('_controller');
By default Request object doesn't contain any information about table because it just passes some parameters to some action attached to some route, and the action interacts with some data model which presents some database table.
I made it (a bit dirty) not the way i wanted it but it works on the current db scale i have.
just added the following code to the addFilterConstraint
if(
$targetEntity->getTableName() == 'table' ||
$targetEntity->getTableName() == 'table2' ||
$targetEntity->getTableName() == 'table3' ||
$targetEntity->getTableName() == 'table4' ||
$targetEntity->getTableName() == 'table5'
){
return true;
}
return $targetTableAlias.'.'.Utils::CLIENT_ID_NAME.' = '. $this->getParameter(Utils::CLIENT_ID_NAME);
p.s in case someone got into this and noticed some strange behavior when using return ""; just replace it with return true; sometimes it happens sometimes not i am still not sure why.

Symfony2 data transformer string to entity (reverseTransform)

I am pretty new to Symfony and hope someone can help me. I have an entity called Material and an associated entity called MaterialKeyword, which are basically tags. I am displaying the keywords comma delimited as a string in a text field on a form. I created a data transformer to do that. Pulling the keywords from the database and displaying them is no problem, but I have a problem with the reversTransform function when I want to submit existing or new keywords to the database.
Material class (MaterialKeyword):
/**
* #Assert\Type(type="AppBundle\Entity\MaterialKeyword")
* #Assert\Valid()
* #ORM\ManyToMany(targetEntity="MaterialKeyword", inversedBy="material")
* #ORM\JoinTable(name="materials_keyword_map",
* joinColumns={#ORM\JoinColumn(name="materialID", referencedColumnName="materialID", nullable=false)},
* inverseJoinColumns={#ORM\JoinColumn(name="keywordID", referencedColumnName="id", nullable=false)})
*/
public $materialkeyword;
/**
* Constructor
*/
public function __construct()
{
$this->MaterialKeyword = new ArrayCollection();
}
/**
* Set materialkeyword
*
* #param array $materialkeyword
*
*/
public function setMaterialkeyword(MaterialKeyword $materialkeyword=null)
{
$this->materialkeyword = $materialkeyword;
}
/**
* Get materialkeyword
*
* #Assert\Type("\array")
* #return array
*/
public function getMaterialkeyword()
{
return $this->materialkeyword;
}
Here is my code from the data transformer:
This part is working:
class MaterialKeywordTransformer implements DataTransformerInterface
{
/**
* #var EntityManagerInterface
*/
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
/**
* Transforms an object (materialkeyword) to a string.
*
* #param MaterialKeyword|null $materialkeyword
* #return string
*/
public function transform($material)
{
$result = array();
if (null === $material) {
return '';
}
foreach ($material as $materialkeyword) {
$result[] = $materialkeyword->getKeyword();
}
return implode(", ", $result);
}
This part is not working:
/**
* Transforms a string (keyword) to an object (materialkeyword).
*
* #param string $materialkeyword
* #return MaterialKeyword|null
* #throws TransformationFailedException if object (materialkeyword) is not found.
*/
public function reverseTransform($keywords)
{
// no keyword? It's optional, so that's ok
if (!$keywords) {
return;
}
$repository = $this->manager
->getRepository('AppBundle:MaterialKeyword');
$keyword_array = explode(", ", $keywords);
foreach($keyword_array as $keyword){
$materialkeyword = new MaterialKeyword();
$keyword_entry = $repository->findBy(array('keyword' => $keyword));
if(array_key_exists(0, $keyword_entry)){
$keyword_entry_first = $keyword_entry[0];
}else{
$keyword_entry_first = $keyword_entry;
}
if (null === $keyword_entry_first) {
throw new TransformationFailedException(sprintf('There is no "%s" exists',
$keywords
));
}
$materialkeyword->setKeyword($keyword_entry_first);
}
return $materialkeyword;
}
There will be several keywords, so how do I store them. I tried Arrays and ArrayCollections (new ArrayCollection()) without any success.
The error that I am getting currently with the code above:
Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in /.../vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 605 and defined
TL;DR;
Your reverseTransform function should return an array containing zero or n MaterialKeyword.
It should not return a single MaterialKeyword object because the reverse transformation of MaterialKeyord[] --> string is not string --> MaterialKeyword, it is string --> MaterialKeyword[].
Thinking about this, the doctrine ArrayCollection exception you have make sense as it is trying to do new ArrayCollection(/** Single MaterialKeyword object */) instead of new ArrayCollection(/** Array of MaterialKeyword objects */).
From what you're telling I assume that Material and MaterialKeyword are connected by a ManyToMany association, in which case each Material has an array of MaterialKeyword objects associated to it.
Which means, that your Data Transformer should work with arrays as well, but you're only working with single objects.
Specifically, reverseTransform should return an array of MaterialKeyword objects, whereas you're only returning one (the last one handled in the loop.)
Another issue is that your method created new objects every time, even though $repository->findBy(...) would already return a MaterialKeyword instance. Creating a new object would cause that entry to be copied instead of simply used.
So the correct method might look like this:
public function reverseTransform($keywords)
{
// no keyword? It's optional, so that's ok
if (!$keywords) {
return array();
}
$repository = $this->manager
->getRepository('AppBundle:MaterialKeyword');
$keyword_array = explode(", ", $keywords);
$result_list = array(); // This will contain the MaterialKeyword objects
foreach($keyword_array as $keyword){
$keyword_entry = $repository->findOneBy(array('keyword' => $keyword));
if (null === $keyword_entry) {
throw new TransformationFailedException(sprintf('There is no "%s" exists',
$keyword
));
}
$result_list[] = $keyword_entry;
}
return $result_list;
}
#Hanzi put me on the correct track. It has to be an array of MaterialKeywords objects.
Here is my final working code in class MaterialKeywordTransformer:
/**
* Transforms a string (keyword) to an object (materialkeyword).
*
* #param string $materialkeyword
* #return MaterialKeyword|null
* #throws TransformationFailedException if object (materialkeyword) is not found.
*/
public function reverseTransform($keywords)
{
// keyword are optional, so that's ok
if (!$keywords) {
return;
}
$repository = $this->manager
->getRepository('AppBundle:MaterialKeyword');
$repository_m = $this->manager
->getRepository('AppBundle:Material');
$keyword_array = explode(", ", $keywords);
foreach($keyword_array as $keyword){
$materialkeyword = new MaterialKeyword();
$materialkeyword->setKeyword($keyword);
if($this->opt["data"]->getMaterialID() !== null) {
$materialkeyword->setMaterialID($this->opt["data"]->getMaterialID());
} else {
$material = $repository_m->findOne();
$materialID = $material[0]->getMaterialID();
$materialkeyword->setMaterialID($materialID);
}
$materialkeywords[] = $materialkeyword;
if (null === $keywords) {
throw new TransformationFailedException(sprintf('There is no "%s" exists',
$keywords
));
}
}
return $materialkeywords;
}

Write GET route in Symfony with FOSRestBundle

I have function
/**
* My function
*
* #FOS\View()
* #FOS\Get("/myfunction/?param1={p1}&param2={p2}&paramn={pn}")
*
* #param integer $param1
* #param integer $param2
* #param integer $paramn
* #return mixed
*/
public function getMyFunction($param1, $param2, $paramn)
{
return new Response($param1. ' ' . $param1. ' ' . $paramn);
}
But when I call http://host/myfunction/?param1=1&param1=2&paramn=3 dosen't work.
What is wrong in definition of function?
UPDATE: New function
/**
* My function
*
* #FOS\View()
* #FOS\Get("/myfunction/")
*
* Request $request
* #return mixed
*/
public function getMyFunction(Request $request)
{
$requestParams = $request->request->all();
return new Response($requestParams['param1']);
}
And, now I call http://host/myfunctin/?param1=1, but still, dosen't work.
Error: "Notice: Undefined index: param1"
Request for get parameters isn't good?
Thanks!
You have to remove the query string parameters from the route.
To get them you have to inject a Request object in the function signature and the use $request->get('parametername') to retrieve then.

Symfony2 locale isn't working properly

After reading documentation and looking for it with Google, I've to ask you.
I want to switch between 3 languages: ca_ES, es_ES and en_GB
So I did a controller like this:
/**
* #Route("/canviar-idioma/{locale}", name="change_lang")
* #Template()
*
* #return array
*/
public function canviarIdiomaAction($locale){
$request = $this->getRequest();
if ($locale == 'cat'){
$this->get('translator')->setLocale('ca_ES');
return new Response('ca');
} else if ($locale == 'es'){
$this->get('translator')->setLocale('es_ES');
return new Response('es');
} else if ($locale == 'eng'){
$this->get('session')->set('_locale', 'en_GB');
return new Response('en');
}
return new Response(null);
}
This controller is being called by ajax, when an user clicks a flag with the language. I receive the "ca" or "es" or "en" correctly, so controller is "working" somehow.
As you can see, I've tried using it by session or getting the translator. Both ways same results.
But, I made this controller to check if my locale really changed:
/**
* #Route("/quinlocaletinc", name="quinlocaletinc")
* #Template()
*
* #return array
*/
public function quinlocaletincAction(){
$request = $this->getRequest();
return new Response($request->getLocale());
}
And this locale ALWAYS gives "ca_ES" as it's the one defined in my parameters file:
locale: ca_ES
And my config.yml:
default_locale: %locale%
translator: { fallback: %locale% }
You need to use the "special" _locale variable in the route, Symfony will then properly set the locale for your application.
You can read more about this in the documentation
Your route should look like this:
/**
* #Route("/canviar-idioma/{_locale}", requirements={"_locale" = "ca_ES|es_ES|en_GB"}, name="change_lang")
* #Template()
*
* #return array
*/
public function canviarIdiomaAction() {
$locale = $request->getLocale();
// ...
Your second route will also need the parameter
/**
* #Route("/quinlocaletinc/{_locale}", name="quinlocaletinc")
* #Template()
*
* #return array
*/
public function quinlocaletincAction() {
$request = $this->getRequest();
return new Response($request->getLocale());
}
A good convention is to prefix all routes with the locale rather than postfix
/**
* #Route("/{_locale}/quinlocaletinc", name="quinlocaletinc")
* #Template()
*
* #return array
*/
public function quinlocaletincAction() {
$request = $this->getRequest();
return new Response($request->getLocale());
}
By using the _locale variable in Symfony, everything just "works" (i.e. if you visit /ca_ES/page all links on that page will include the right url).
Also when using the _locale parameter in your route, $this->get('translator')->setLocale('ca_ES'); is un-necessary as it will happen automatically.
Your annotation routing and Controller argument should be {_locale} and $_locale.
/**
* #Route("/canviar-idioma/{_locale}", name="change_lang")
* #Template()
*
* #return array
*/
public function canviarIdiomaAction($_locale)
{
// ...

Create new type in symfony2

How can I create FormType in Symfony2 for converting an entity to a string and back?
I've done all that is saying in here but there is an error:
Expected argument of type "string", "<Vendor>\<Bundle>\Entity\User" given
How can I create a form where a text field will be converted to an user object?
Assuming User has an username field i would do a transform like the following. Please pay attention that transform is for User to string transform, while reverseTransform is the opposite.
Add the transformer to your form field:
$builder
->add('user', 'text')
->addViewTransformer($transformer)
Relevant code (like example you've cited):
/**
* Transforms an User to a string.
*
* #param User|null $user
* #return string
*/
public function transform($user)
{
return $user ? $user->getUsername() : '';
}
/**
* Transforms a string to an User.
*
* #param string $username
* #return User|null
*/
public function reverseTransform($username)
{
if(empty($username)) return null;
$user = $this->om
->getRepository('AcmeHelloBundle:User')
->findOneBy(array('username' => $username))
;
return $user; // Can be null
}
You can extract this form type here and use that. https://github.com/symfony/symfony/pull/1951 it does what you are asking.

Resources