Symfony route generation with multi-valued query string parameter - symfony

Using Symfony 4.4, I'm trying to generate a route with a query string using the following code:
$this->get('router')->generate('index', ['status' => ['a', 'b']]);
That generates .../?status[0]=a&status[1]=b. I would have expected it to rather be .../?status[]=a&status[]=b.
This is not a huge problem because the URL generated works fine but is rather inconsistent with forms as if a GET form has a multi-valued status field, submitting it would go to the latter URL.
Symfony 5.3 seems to behave similarly.
Is there a way to tell the router not to use indices in multi-valued query string parameters?

Symfony UrlGenerator uses http_build_query to build query. So is not possible to change easily the behavior.
It may be possible to override/extend actual generator but I don't think is a good idea.

Related

Azure Mobile Apps - Overriding the QueryAsync method with custom code in Table Controller

I would like to override the Query Async Method with some Custom code, so I can access an external api, get some data and then use this data to query the db tables to get the results, this should support all the default sync and paging features provided by the base method. I'm using the latest 5.0 DataSync library. Old versions returned a IQueryable so this was easy to do, but now it returns an action result. Is there any solution? Could not find any docs.
e.g. I get a set of guids from api. I query the db tables with these guids and get all the matching rows and return back to client.
I know how to override the method, call external api's, get data and query the db tables, but this provides me with a custom data format not the one that the query async gives by default with paging support.
The way to accomplish this is to create a new repository, swapping out the various CRUD elements with your own implementation. You can use the EntityTableRepository as an example. You will note that the "list" operation just returns an IQueryable, so you can do what you need to do and then return the updated IQueryable.

API Platform custom IRI with value objects

I am currently trying to create a custom IRI for one of my entities in API Platform.
I know there is page in the documentation describing how to use a custom IRI (https://api-platform.com/docs/core/identifiers/), but I can't get it working.
My entity uses a value object for the id (currently used for IRI) and also for the name (should be used for IRI). But the values themself are priviate and scalar in the entity.
API Platform seems to get the information what should be used as the identifier, from my XML Doctrine mapping. I already tried to overwrite it by usung annotations, attributues and YAML definitions. Without luck.
The returned error reads:
preg_match(): Argument #2 ($subject) must be of type string
(at this point it receives the value object instead of the actual value)
best regards,
spigandromeda
I solved my problem.
To explain the solution, I have to dig a little into API Platform response generation.
API platform generates an IRI for every entity it returns (colelction and item operation)
it's using the Symfony router go generate the URI
all the necessary information can draw API Platform from different sources (YAML, XML, annotations, attributes)
the information include the identifier(s) defined for the entities resource
API Platform gets the value for the identifier via Symfony property accessor
because the property accessor is using getters before accessing private properties via reflection, it will return the VO
an ordinary VO cannot be used by the Symfony URL generator to create the URL
As I explained, I am using a VO for my Id as well. So I tried to figure out why it was working with the Id VO but not with the name VO.
Simple answer: the Id VO implemented the __toString method and the name VO didn't. So the solution was to let the name VO implement this method as well.
It was interesing to dig into the internal process of API Platform, but I also feel a little stupid :D

How to have a Symfony API GET call return an embedded json object instead of an IRI

I have been working on a Symfony API with Api Platform and have autogenerated all the endpoints which works fine. Except for one thing, when GETting a entity with a child entity, the child entity is not given in json but in an IRI format so this means that we get "/api/locations/1" instead of a JSON Object. I have been trying for hours but can't figure out how to change this. We are using annotations for the routes and the database relations.
You can use some nice tool like POSTMAN.
Then you can see the JSON response pretty printed.
You have to use the same, common normalization group for all embeded entities as desribed in docs.
The same is required for GraphQL when you need embeded fields other than id - asking for more than available results "Internal server error" "Cannot return null for non-nullable field XXX.YYY" - using GraphiQL helps with debugging more than POSTMAN (great tool) - when GraphQL will be working as expected, REST should be OK, too.
Unfortunatelly standard admin-on-rest doesn't like 'already fetched' values, expecting strings/IRIs only, not objects.
You can choose the response content type:

How to filter in Symfony2?

First of all i am very new to Symfony 2 and started to learn.
Is there any possibility for filter a value? Perhaps filter chains too?
I know this concept from Zend Framework 1 and 2.
E.g.:
i have a string "1A- N "
now i want to filter so that only numeric values pass; result "1"
Do i have to implement this on my own i Symfony?
I would like to do something like:
$text = '1A - N';
$numberFilter = new NumberFilter();
$filteredText = $numberFilter->filter($text);
//now in $text i find '1'
But for now i nowhere found something like this in Symfony what surprises me a lot. I thought it is a full stack framework and such function is so basic.
I found something like validators but they only say if a value e.g. contains only numbers or not. Or is the validation concept of symfony like that it does not only say if it is numeric or not but filter all other smybols out, too?
Depending on what you want precisely:
disallow user input not fulfilling certain rules
use validators in forms
use asserts in entities
chenge user input in case it's wrong
use viewransformers in forms
use event listeners in forms
use event listeners for doctrine
change data that already exists in the database
use filters in twig
create a command to execute from commandline
You can also try http://php.net/manual/ro/filter.filters.sanitize.php
I have built quite big apps with Symfony and never needed such a feature. Filters are mostly used in views anyway. Symfony comes with Twig, which has filters, that can be chained, and you can write your own filters. But if you need filters in the backend to do some background processing, you can accomplish it the way you suggested.
I suggest you write an interface and use a factory pattern, so you set a standard if you make many filters, so it will be easier to make the chaining work;)
After the answers and searching i got to the following conclusion.
There is no concept for this for now in Symphony 2.
You have to write it on your own.

Symfony2: Why weren't query string parameters included in the routing component?

I am porting a legacy application to Symfony2 and I am struggling because routing doesn't include query string parameters. Some quick examples: Suppose you have a search page for books where you can filter results based on criteria:
http://www.bookstore.com/books?author=Stephen+King&maxPrice=20
The nice thing about query string parameters in a case like this is you can have any number of filters, use the ones you want for any given request, and not crowd the URL with filters you're not using.
Let's say we rewrote the routing for the above query using the Symfony2 routing component. It might look like this:
http://www.mybookstore.com/book/any_title/stephen%20king/any_release_date/max_price_20/any_min_price/any_format/any_category
Even not taking into account how arbitrarily long an unclean that URL is I still don't think it is as intuitive because each 'segment' of that route is not a key value pair but instead just a value (e.g. author=Stephen+King > /stephen%20king/).
You can of course access query string parameters in the controller by passing the Request object into the action method (e.g. indexAction(Request $request) {) but then validating them and passing them into other parts of the application becomes a hassle (i.e. where I find myself now). What if you are using the Knp Menu Bundle to build your sidebar and you want parts to be marked as .current based on query string parameters? There is no functionality for that, just functionality to integrate with Symfony2 routes.
And how to validate that your query string parameters are acceptable? I am currently looking at validating them like a form to then pass them into the database to generate a query. Maybe this is the way the Symfony2 team envisioned handling them? If so I'd just really like to know why. It feels like I'm fighting the application.
I ended up actually asking Fabien this question at Symfony Live San Francisco 2012. There was another talk at the same conference in regards to this question and I will share with you the slides here:
http://www.slideshare.net/Wombert/phpjp-urls-rest#btnNext
Basically in the slides you can see that the author agrees with me in that query string parameters should be used for filtering. What they should not be used for is determining a content source. So a route should point to a product (or whatever) and query string parameters should then be used in your controller to apply whatever logic you require to filter that content source (as per Fabien).
I ended up creating an entity in my application that I bind all my query string parameters to and then manipulate, much the same way forms are handled. In fact when you think about it it's basically the same thing.
Like in Symfony1, query strings are independent from the route parameters.
If you have a path defined as #Route("/page/{id}", name="single_page"), you can create a path in your view like this:
{{ path('single_page', { id: 3, foo: "bar" }) }}
The resulting URL will be /page/3?foo=bar.

Resources