Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Request Object & Query OR Logic

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Because astronauts love to debate news, our site will have a lot of comments on production. So, let's add a search box above this table so we can find things quickly.

Open the template and, on top, I'm going to paste in a simple HTML form:

... lines 1 - 6
{% block content_body %}
<div class="row">
<div class="col-sm-12">
<h1>Manage Comments</h1>
<form>
<div class="input-group mb-3">
<input type="text"
name="q"
class="form-control"
placeholder="Search..."
>
<div class="input-group-append">
<button type="submit"
class="btn btn-outline-secondary">
<span class="fa fa-search"></span>
</button>
</div>
</div>
</form>
... lines 27 - 57
</div>
</div>
{% endblock %}

We're not going to use Symfony's form system because, first, we haven't learned about it yet, and second, this is a super simple form: Symfony's form system wouldn't help us much anyways.

Ok! Check this out: the form has one input field whose name is q, and a button at the bottom. Notice that the form has no action=: this means that the form will submit right back to this same URL. It also has no method=, which means it will submit with a GET request instead of POST, which is exactly what you want for a search or filter form.

Let's see what it looks like: find your browser and refresh. Nice! Search for "ipsam" and hit enter. No, the search won't magically work yet. But, we can see the ?q= at the end of the URL.

Fetching the Request Object

Back in the controller, hmm. The first question is: how can we read the ?q query parameter? Actually, let me ask some bigger questions! How could we read POST data? Or, headers? Or the content of uploaded files?

Science! Well, actually, the request! Any time you need to read information about the request - POST data, headers, cookies, etc - you need Symfony's Request object. How can you get it? Well... you can probably guess: add another argument with a Request type-hint:

... lines 1 - 5
use Symfony\Component\HttpFoundation\Request;
... lines 7 - 9
class CommentAdminController extends Controller
{
/**
* @Route("/admin/comment", name="comment_admin")
*/
public function index(CommentRepository $repository, Request $request)
{
... lines 17 - 22
}
}

Important: get the one from HttpFoundation - there are several, which, yea, is confusing:

... lines 1 - 5
use Symfony\Component\HttpFoundation\Request;
... lines 7 - 25

So far, we know of two "magical" things you can do with controller arguments. First, if you type-hint a service class or interface, Symfony will give you that service. And second, if you type-hint an entity class, Symfony will query for that entity by using the wildcard in the route.

Well, you might think that the Request falls into the first magic category. I mean, that the Request is a service. Well, actually... the Request object is not a service. And, the reasons why are technical, and honestly, not very important. The ability to type-hint a controller argument with Request is the third "magic" trick you can do with controller arguments. So, it's (1) type-hint services, (2) type-hint entities or (3) type-hint the Request class. There is other magic that's possible, but these are the 3 main cases.

Oh, side-note: while the Request object is not in the service container, there is a service called RequestStack. You can fetch it like any service and call getCurrentRequest() to get the Request:

public function index(RequestStack $requestStack)
{
    $request = $requestStack->getCurrentRequest();
}

Anyways, the request gives us access to everything about the... um, request! Add $q = $request->query->get('q'):

... lines 1 - 9
class CommentAdminController extends Controller
{
/**
* @Route("/admin/comment", name="comment_admin")
*/
public function index(CommentRepository $repository, Request $request)
{
$q = $request->query->get('q');
... lines 18 - 22
}
}

This is how you read query parameters, it's like a modern $_GET. There are other properties for almost everything else: $request->headers for headers, $request->cookies, $request->files, and a few more. Basically, any time you want to use $_GET, $_POST, $_SERVER or any of those global variables, use the Request instead.

A Custom Query with OR Logic

Now that we have the search term, we need to use that to make a custom query. So, sadly, we cannot use findBy() anymore: it's not smart enough to do queries that use the LIKE keyword. No worries: inside CommentRepository, add a public function called findAllWithSearch(). Give this a nullable string argument called $term:

... lines 1 - 15
class CommentRepository extends ServiceEntityRepository
{
... lines 18 - 34
public function findAllWithSearch(?string $term)
{
... lines 37 - 49
}
... lines 51 - 79
}

I'm making this nullable because, for convenience, I want to allow this method to be called with a null term, and we'll be smart enough to just return everything.

Above the method, add some PHP doc: this will @return an array of Comment objects:

... lines 1 - 15
class CommentRepository extends ServiceEntityRepository
{
... lines 18 - 30
/**
* @param string|null $term
* @return Comment[]
*/
public function findAllWithSearch(?string $term)
{
... lines 37 - 49
}
... lines 51 - 79
}

Ok: we already know how to write custom queries: $this->createQueryBuilder() with an alias of c:

... lines 1 - 15
class CommentRepository extends ServiceEntityRepository
{
... lines 18 - 30
/**
* @param string|null $term
* @return Comment[]
*/
public function findAllWithSearch(?string $term)
{
$qb = $this->createQueryBuilder('c');
... lines 38 - 49
}
... lines 51 - 79
}

Then, if a $term is passed, we need a WHERE clause. But, here's the tricky part: I want to search for the term on a couple of fields: I want WHERE content LIKE $term OR authorName LIKE $term.

How can we do this? Hmm, the QueryBuilder apparently has an orWhere() method. Perfect, right? No! Surprise, I never use this method. Why? Imagine a complex query with various levels of AND clauses mixed with OR clauses and parenthesis. With a complex query like this, you would need to be very careful to use the parenthesis in just the right places. One mistake could lead to an OR causing many more results to be returned than you expect!

To best handle this in Doctrine, always use andWhere() and put all the OR logic right inside: c.content LIKE :term OR c.authorName LIKE :term. On the next line, set term to, this looks a little odd, '%'.$term.'%':

... lines 1 - 15
class CommentRepository extends ServiceEntityRepository
{
... lines 18 - 30
/**
* @param string|null $term
* @return Comment[]
*/
public function findAllWithSearch(?string $term)
{
$qb = $this->createQueryBuilder('c');
if ($term) {
$qb->andWhere('c.content LIKE :term OR c.authorName LIKE :term')
->setParameter('term', '%' . $term . '%')
;
}
... lines 44 - 49
}
... lines 51 - 79
}

By putting this all inside andWhere() - instead of orWhere() - all of that logic will be surrounded by a parenthesis. Later, if we add another andWhere(), it'll logically group together properly.

Finally, in all cases, we want to return $qb->orderBy('c.createdAt', 'DESC') and ->getQuery()->getResult():

... lines 1 - 15
class CommentRepository extends ServiceEntityRepository
{
... lines 18 - 30
/**
* @param string|null $term
* @return Comment[]
*/
public function findAllWithSearch(?string $term)
{
$qb = $this->createQueryBuilder('c');
if ($term) {
$qb->andWhere('c.content LIKE :term OR c.authorName LIKE :term')
->setParameter('term', '%' . $term . '%')
;
}
return $qb
->orderBy('c.createdAt', 'DESC')
->getQuery()
->getResult()
;
}
... lines 51 - 79
}

Remember, getResult() returns an array of results, and getOneOrNullResult() returns just one row.

Phew! That looks great! Go back to the controller. Use that method: $comments = $repository->findAllWithSearch() passing it $q:

... lines 1 - 9
class CommentAdminController extends Controller
{
/**
* @Route("/admin/comment", name="comment_admin")
*/
public function index(CommentRepository $repository, Request $request)
{
$q = $request->query->get('q');
$comments = $repository->findAllWithSearch($q);
... lines 19 - 22
}
}

Moment of truth! First, remove the ?q= from the URL. Ok, everything looks good. Now search for something very specific, like, ahem, reprehenderit. And, yes! A much smaller result. Try an author: Ernie: got it!

Woo! This is great! But, we can do more! Next, let's learn about a Twig global variable that can help us fill in this input box when we search. Then, it's finally time to add a join to our custom query.

Leave a comment!

34
Login or Register to join the conversation
Farshad Avatar
Farshad Avatar Farshad | posted 2 years ago

Offtopic: When I do 'symfony console make:migration' it says: This behaviour is (currently) not supported by Doctrine 2.

1 Reply

Hey @Farry7!

Hmmm. I’m not familiar with this error! Is this from a custom project?

So, looking into this - https://stackoverflow.com/q... - it appears that this might happen if you have some sort of typo in your entity annotations. Unfortunately, the error isn’t very clear. But I would check your Annotations carefully to see if you can spot any issues!

Cheers!

Reply
Default user avatar
Default user avatar Chris | posted 4 years ago | edited

Hello,
a question about Request: Is this safe or prefiltered in any way? Or asked more clearly: What advantage does $request->query->get('q'); have over $_GET['q']?

1 Reply
Victor Avatar Victor | SFCASTS | Chris | posted 4 years ago | edited

Hey Chris!

This is just a helpful wrapper around $_GET super global variables, that except of getting a value may help you to provide a default value if the parameter key does not exist like:


$request->query->get('q', 'your-default-value-if-needed');

And except this get() method it also has getInt(), getBoolean(), has(), etc that allow you to write less custom code.

And this is just best practice to work with Request object instead of super global variables directly.

Cheers!

1 Reply
Oliver-W Avatar
Oliver-W Avatar Oliver-W | posted 2 years ago

Hi,

what if I wanted to search over the date too? But only on the date, not the time!

The $qb->andWhere('c.content LIKE :term OR c.authorName LIKE :term' OR CONVERT(c.publishedAt, DATE) LIKE :term) produces: Error: Expected known function, got 'CONVERT'!?

Thx

Reply

Hey Oliver,

Yeah, there's limited set of SQL functions that will work. Probably, the most easiest solution would be to search by time range, e.g. if you need to search all entries for today, September 1st - add a custom code before the query to know if you have a valid date or no, something like:


$startOfTheDay = new \DateTime();
$endOfTheDay = new \DateTime();
if (preg_match('/\d\d\d\d-\d\d-\d\d/', $term)) {
    // Looks like term is a date, create an object
    $startOfTheDay = new \DateTime($term.' 00:00:00');
    $endOfTheDay = new \DateTime($term.' 23:59:59');
}

$qb
    // ...
    ->andWhere('c.content LIKE :term OR c.authorName LIKE :term' OR (c.publishedAt >= :startOfTheDay AND c.publishedAt <= :endOfTheDay) ')

But probably for date/time ranges I'd recommend to add a new field for this. And yeah, it was just an example, you probably may want more complex or optimized regex pattern. And for operations with date I'd recommend Carbon library that makes it much easier: https://carbon.nesbot.com/docs/

I hope this helps!

Cheers!

Reply
Brandon Avatar
Brandon Avatar Brandon | posted 3 years ago

Hello, this is awesome I have everything working. Can you point me to a tutorial on making this all work with ajax?

Reply

Hey Brandon

Well done! You can watch this tutorial, it's unrelated but teaches to work with AJAX and forms https://symfonycasts.com/sc...
or you can watch our ReactJS tutorial if you want to dive deeper in the the Frontend world https://symfonycasts.com/sc...

Cheers!

Reply
Akavir S. Avatar
Akavir S. Avatar Akavir S. | posted 3 years ago

Hello
What if i want to search something with multiples criteria ?

for exemple i want to search all the articles wrote by 'mike' and 'Kenneth' ?

Did you created a tutoriel for thoses cases ?

Cheers !

Reply

Hey Virgile,

I'm not sure if we cover it somewhere, but I think this course might be interesting for you: https://symfonycasts.com/screencast/doctrine-queries - we cover how to write complex queries there.

But basically, you just need to write more complex andWhere() clause where you can combine a few simple clauses with OR condition. For example, something like this:


->andWhere('u.author IN (:authors)')
->setParameter('authors', ['Mike', 'Kenneth'])

You can also use our feature-rich search to find more ideas of how to write addWhere() conditions: https://symfonycasts.com/search?q=andWhere&amp;sort=most relevant&amp;page=1&amp;types=code_block

For more complex things and better (relevant) search results you would need to use a special search engine like e.g. ElasticSearch that we use on SymfonyCasts. But we don't have a screencast about it. Thought we would like to record one in the future, but no any estimations yet.

I hope this helps!

Cheers!

2 Reply
Mike P. Avatar

It would really be great if you could make a Elasticsearch / some similar tutorial!
Ryan is such a profound teacher and this "feature", a "complete" search tutorial, is really missing for SF on this site.

At the moment, for my own project, I'am using "like" querys, because "match() against()" is slower.
But this "manual" approach is nothing compared to elastic search.
I would really appreciate a tutorial about it as well!

Reply

Hey Mike,

Thank you for your interest in SymfonyCasts tutorials! Yes, ElasticSearch is one of many tutorials we would like to release some day, but unfortunately we don't have any plans for it yet. Btw, could you share with us what exactly topics you would like to see in this course?

Cheers!

Reply
Mike P. Avatar

Great to hear that it’s on your todo!

1.) Best method of integrating it into Symfony (Have you used the FOS Bundle or did you “manually” integrate it into Symfonycasts website?)

2.) An advanced use case, that I implemented myself with “match against” and “member of” and would like to do so instead with elastic search:
How to filter many to many connections with it

By example:
A user wants to filter articles by categories, many categories can be selected and only articles get shown that are in *all* selected categories

Bonus:
The category filter multiselect element on the page gets updated to show only categories that have articles in it. By example: A website does have 3 categories (Earth, Mars, Sun) with one Article which is in the “Mars” and “Sun” category. A User now searches for all articles (“blank” search term) and filters the results by category “mars”. Now the website is only showing the “sun” filter as available filter. The “earth” filter is grayed out/disabled/hidden, because there would be no articles with “Mars” and “Earth”.

3.) Optional but interesting:

Scaling: How to scale elastic search instances for many users

If I can assist you in any another way I will be happy to do so :)

1 Reply

Hey Mike,

Thank you for sharing with us this! We will definitely consider this when start working on ElasticSearch course. But I don't have any estimations for you yet, it's not on our TODO list for the nearest future unfortunately. But a lot of new screencasts about Symfony 5 is coming ;)

Cheers!

1 Reply
Mike P. Avatar

Great! I want to suggest one topic for your SF5 course:
A search function based on user input (By example: Search in article content & article slugs for a $searchTerm (which can contain > 1 word))

The reasons why I think many others and I would profit big time if you would cover this topic:

1.) Since a "search bar" is so common nowadays, its a "generic" feature that a lot of people need & want to learn about
2.) I found out myself, that building it yourself, can be a real hassle.
a.) Its a "blind spot" on symfonycasts, since you don't have (or I have not seen it yet?) any course regarding a real "searchbar" (Like you said, Elasticsearch is a considered topic, but not in the nearest future)
b.) Easy solutions like querys with "like %$term%" are not recommended by all the tutorials I've found on google (performance killer). Statements like "Match() Against()" have to be implemented via Bundles (Beberlei Doctrine Extension), a mysql index have to be created via @Entity annotation and even if you do all this, you are not done, you have to escape the $searchTerm manually, because doctrine doesnt have a "build in" function (http://disq.us/p/27a2v1x)

Since Symfonycasts does have the highest quality and broadest selection of video tutorials for SF, I was surprised that you don't have tutorials for this kind of topic yet. (Or have I missed it?)

Thanks for your time SF Casts Team! :)

1 Reply

Hey Mike,

Ah, I see! Yeah, it might be tricky, we do cover some simple things with LIKE query in a few tutorials like:
https://symfonycasts.com/sc...
https://symfonycasts.com/sc...

Yes, using "LIKE" might be slow...but as you said "LIKE" is pretty simple to use. Well, first of all it's slow only on huge tables, and I bet if you would add indexes - it might be still a good speed for you, but of course you need to profile and see if it's ok or not for you. What about more complex solutions like using MATCH AGAINST - I will add this topic to our idea list, but I'm not sure it might be released soon as well.

Also, it's always a thin edge where you use simple "LIKE" and if you're not enough with it - usually devs move to specific solutions like ElasticSearch instead of doing something complex with MySQL abilities, first of all because of the speed. So really, if you're not enough that simple "LIKE" and you need something more complex for performing a search in a huge data set - I'd recommend you to look right into ElasticSearch or similar solutions instead of complicate things with SQL.

And probably for this same reason, I'm not sure if we would show something complex with MySQL other than "LIKE", we would better spend this time working on an advanced search solution like ElasticSearch. In other words, IMO ElasticSearch course has more chances to be released than a course about doing complex search natively in MySQL, that might just not worth it.

Anyway, thanks for sharing some more ideas on this topic! I'll try to discus this with the team as well.

Cheers!

1 Reply
Juliano P. Avatar
Juliano P. Avatar Juliano P. | Victor | posted 3 years ago

It would be great to have some casts about ElasticSearch, actually, I'm quite surprised that in 2020 you don't have one yet.

Reply

Hey Juliano,

Agree, we have ElasticSearch course on our TODO list, but it's low priority for now unfortunately, so I don't have any estimations when it might be released. Btw, what topics exactly would you like to see in that course? Probably something that is not covered or covered badly in other tutorials over the internet? Actually, I believe there're some good courses on the internet about it. We don't want to make just "yet another ElasticSearch" course. If you may give us some good topics you would like to see in this course that you don't see in others - it would be awesome.

Cheers!

1 Reply
Juliano P. Avatar
Juliano P. Avatar Juliano P. | Victor | posted 3 years ago

Hey Victor! Thanks for your answer. There are a quite few tutorials how to use Sf + ES, not so many. However, none of these go beyond the basics that you could find in the documentation. Other problem is that almost all these tutorials use the FOS bundle or other package, that unfortunately, are not updated and doesn't support last versions of ES nor Sf.
Also, another issue with most tutorials / packages, they are meant to use ES as a secondary DB, only for search purposes, nowadays isn't uncommon use only ES as your main DB.

I would argue that a good cast could cover these GAP's I mentioned before using the official Elasticsearch library and integrate it with Sf 5.

Reply

Hey Juliano,

Ah, I see. Thank you for sharing your thoughts on this topic! It's sad to hear bundles with ES are badly maintained :/ It's mostly driven by community, and it's pretty popular bundle. Not sure why it's so.

Anyway, I'll add your thoughts to our ES ticket, it will be definitely helpful when we will start working on this tutorial. Unfortunately, I don't have any estimations yet.

Cheers!

Reply
Lijana Z. Avatar
Lijana Z. Avatar Lijana Z. | posted 4 years ago

nice, in ->andWhere() I did not think that I can use OR word to have more conditions :) I used to do like $qb->expr()->orX() which form me looks too long for such simple thing and less readable.

But ->setParameter('term', '%' . $term . '%') looks like too much typing. I wish it automatically add % signs when for LIKE conditions. I dont remember using LIKE without %% and not even sure what would be the result.

Reply

Hey Lijana Z.

Yeah, I don't like using expressions either, they are just hard to read (IMHO), but yes, you can add OR conditions inside an andWhere(). About auto adding "%" to a LIKE condition. I don't think that's a good idea because there are other kind of wildcards for a LIKE condition that you may want to use (https://www.w3schools.com/sql/sql_wildcards.asp )

Cheers!

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 4 years ago

I want to order my comments by relevance to query. So how do the search engines (Google).

Can i do this?

Or sorting is possible only for:
->orderBy('c. createdAt', 'DESC');

Reply

Hey Dmitriy,

Good question! Actually, this's done by good powerful engines like ElasticSearch - you can google it. Unfortunately, we don't have a screencasts about it, though we plan to release one in the future. And ElasticSearch is just a one of many engines that can give you relevant results. But, fairly speaking, it's quite complex at the first sight, requires some work because you need to correctly index your website and, of course, requires some resources on your production server.

Btw, you can even enable google search on your website, though search results will be rendered in google styles and you'll have advert in the result output. If you don't want to see adverts - you have to pay for this.

If you don't need very cool search, or your users use the search rare on your website - probably you can try to do something similar yourself, for example, execute a few queries that first search more relevant results, like search by titles and headers, and another query that search for description and other minor things and then with PHP script do some merge, i.e. remove duplicated results, combine results correctly, etc. - well, it won't be perfect, but sometimes it might be enough.

Btw, you can try our search on SymfonyCasts - it's implemented with ElasticSearch :)

I hope it helps.

Cheers!

3 Reply
Mike P. Avatar

A ScreenCast about how to implement ElasticSearch into Symfony would be AWESOME +1

1 Reply

Hey Mike,

Yes, it's on our TODO list, we do really want to have it in the future but, unfortunately, I don't have any estimations yet when it might be released.

Cheers!

1 Reply
Akavir S. Avatar

+1 To mike, that would be awesome indeed !

Reply

Hey Virgile,

Thank you for your interest in new SymfonyCasts courses! Your voice is counted :)

Cheers!

1 Reply
Victor N. Avatar
Victor N. Avatar Victor N. | posted 5 years ago

Hey!

I have a question regarding this specific line of code:

`->setParameter('term', '%'.$term.'%')`

Is it safe from SQL injection?

Because paranoya strikes often, I turned that code into

`->setParameter('term', '%'.addcslashes($term, '%_').'%')`

Is it ok? If it's not. Can you please tell me why?

Looking forward to your answear and more awesome courses.

Reply

Hey Victor N.

If you inject your variables to the query using the QueryBuilder $qb->setParameter('name', $name);, then you are totally protected from SQL injection. You only have to be paranoid when you are doing it by your own (Executing a handmade query for example)

Cheers!

2 Reply
Dirk Avatar
Dirk Avatar Dirk | posted 5 years ago | edited

Another wonderful lesson! I was wondering, what is the preferred method to create parameters in Twig when you want to have a simple anchor tag? I am asking because I have using this search query and extended it taking two more arguments, one for an attribute to sort on and one for the sort order.
In my controller this looks like:


$q = $request->query->get('q');
$sortAttribute =  $request->query->get('sortAttribute', 'p.createdAt');
$sortOrder = $request->query->get('sortOrder', 'DESC');
$toSortURL = '?q='.$q.'&'.'sortAttribute=p.';
$sortOrderURL = '&sortOrder=';

In twig what I have is:


a href="{{ toSortURL }}name{{ sortOrderURL }}ASC"> Sort ASC < / a> -
a href="{{ toSortURL }}name{{ sortOrderURL }}DESC"> Sort DESC < / a>

Edit: I had to edit this comment because Disqus kept on rendering the HTML
This all works well as far as I can tell, it just looks ugly and surely there is a better method, right?

Reply
Victor Avatar Victor | SFCASTS | Dirk | posted 5 years ago | edited

Hey Dirk,

Actually, you can use router to generate links with extra parameters, even if route does not have it - they will be added after question mark in your URL. So in controller:


$this->generateUrl('route_name', [
    'sortOrder' => 'DESC',
    'sortAttribute' => 'p',
]);

or in Twig templates:


{{ path('route_name', {'sortOrder' => 'DESC', 'sortAttribute' => 'p'}) }}

If you route has some required parameters - no problem, just pass required params first and then pass extra parameters, e.g.:


$this->generateUrl('article_show', [
    // List all required params first
    'slug' => 'article-slug',
    // Add any extra params
    'sortOrder' => 'DESC',
    'sortAttribute' => 'p.createdAt',
]);

So as you see you just need to pass actual parameters values, i.e. $sortAttribute and $sortOrder to your template. But you know what? You actually may use request object in Twig templates as well, so it would be something like:


{{ path('route_name', {'sortOrder' => app.request.query.get('sortOrder'), 'sortAttribute' => app.request.query.get('sortAttribute')}) }}

I hope I understand you right and it makes sense for you.

Cheers!

3 Reply
Mike P. Avatar

And is this the correct way for the "security check"?

CommentAdminController:


// Sort by
$sortBy = $request->query->get('s', 'DESC');

$queryBuilder = $repository->getWithSearchQueryBuilder($sortBy, $term);

CommentRepository:


$availableSortTerms = [
'ASC',
'DESC'
];

// Security check if orderBy exists
if (!in_array($orderBy, $availableSortTerms)) {
$orderBy = 'DESC';
}

...


return $qb
->orderBy('c.createdAt', $orderBy)
;

Would this be it?

Reply

Hey Mike,

It feels like a defensive programming, but probably a good idea, I like it!

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

The course is built on Symfony 4, but the principles still apply perfectly to Symfony 5 - not a lot has changed in the world of relations!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
        "knplabs/knp-paginator-bundle": "^2.7", // v2.7.2
        "knplabs/knp-time-bundle": "^1.8", // 1.8.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.1.4
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0", // v4.0.14
        "twig/extensions": "^1.5" // v1.5.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "fzaninotto/faker": "^1.7", // v1.7.1
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/maker-bundle": "^1.0", // v1.4.0
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.4
    }
}
userVoice