Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Fetching Data & The Repository

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 $12.00

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

Login Subscribe

Our question table has data! And each time we refresh, we got more data! You get a question! You get a question!

Copy the slug from the latest one and then go to /questions/that-slug to see it. Except... this is not actually that question. The name is kinda right... but that's it. Over in the show() action, this is because nothing is being loaded from the database. Lame!

Here's our next mission: use the $slug to query for a row of Question data and use that to make this page truly dynamic. How? The entity manager that we use to save data can also be used to fetch data.

The Repository

Start by adding a third argument: EntityManagerInterface $entityManager. This interface has a bunch of methods on it. But... most of the time, you'll only use three: persist() and flush() to save, and getRepository() when you want to get data.

... lines 1 - 12
class QuestionController extends AbstractController
{
... lines 15 - 70
public function show($slug, MarkdownHelper $markdownHelper, EntityManagerInterface $entityManager)
{
... lines 73 - 94
}
}

Say $repository = $entityManager->getRepository() and pass the entity class that we want to query. So Question::class.

... lines 1 - 12
class QuestionController extends AbstractController
{
... lines 15 - 70
public function show($slug, MarkdownHelper $markdownHelper, EntityManagerInterface $entityManager)
{
if ($this->isDebug) {
$this->logger->info('We are in debug mode!');
}
$repository = $entityManager->getRepository(Question::class);
... lines 78 - 94
}
}

Whenever you need to get data, you'll first get the repository for an entity. This repository object is really really good at querying from the question table. And it has several methods to help us.

For example, we want to query WHERE the slug column equals the $slug variable. Do that with $question = $repository-> and... this auto completes a bunch of methods. We want findOneBy(). Pass this an array of the WHERE statements we need: 'slug' => $slug. After, dd($question).

... lines 1 - 12
class QuestionController extends AbstractController
{
... lines 15 - 70
public function show($slug, MarkdownHelper $markdownHelper, EntityManagerInterface $entityManager)
{
... lines 73 - 76
$repository = $entityManager->getRepository(Question::class);
$question = $repository->findOneBy(['slug' => $slug]);
dd($question);
... lines 80 - 94
}
}

Ok, let's see what this returns! Refresh and... woohoo! This gives us a Question object. Doctrine finds the matching row of data and uses that to populate an object, which is beautiful.

The repository has a number of other methods on it. For example, findOneBy() returns a single object and findBy() returns an array of objects that match whatever criteria you pass. The findAll() method returns an array of all Question objects and there are a few others. So without doing any work, we can easily execute the most basic queries. Now, eventually we will need to do more complex stuff - and for that, we'll write custom queries. We'll see that later.

404 On Not Found

So when Doctrine finds a matching row, we get back a Question object. But if we change the slug in the URL to something that does not exist, we get null. So: a Question object or null.

Let's think: what do we want to do when someone goes to a URL that doesn't match a real question? The answer is: trigger a 404 page! Great! Um... how do we trigger a 404 page in Symfony?

First, this is optional - I'm going to say /** space and then type Question|null.

... lines 1 - 12
class QuestionController extends AbstractController
{
... lines 15 - 70
public function show($slug, MarkdownHelper $markdownHelper, EntityManagerInterface $entityManager)
{
... lines 73 - 77
/** @var Question|null $question */
$question = $repository->findOneBy(['slug' => $slug]);
... lines 80 - 98
}
}

This simply helps my editor know that this is a Question object or null, which will assist auto-completion. And, to be honest, PhpStorm is so smart that... I think it already knew this.

Below, if not $question, trigger a 404 page by saying throw $this->createNotFoundException(), which is a method on the parent AbstractController class. Pass this any message you want:

No question found for slug %s

And pass the $slug variable.

... lines 1 - 12
class QuestionController extends AbstractController
{
... lines 15 - 70
public function show($slug, MarkdownHelper $markdownHelper, EntityManagerInterface $entityManager)
{
... lines 73 - 77
/** @var Question|null $question */
$question = $repository->findOneBy(['slug' => $slug]);
if (!$question) {
throw $this->createNotFoundException(sprintf('no question found for slug "%s"', $slug));
}
... lines 83 - 98
}
}

That's it! But notice the throw. createNotFoundException() instantiates an exception object - a very special exception object that triggers a 404 page. Most of the time in Symfony, if you throw an exception, it will cause a 500 page. But this special exception maps to a 404.

Let's try it: refresh and... yes! You can see it up here: "404 Not found" with our message.

Two things about this. First: this is the development error page. If we changed the environment to prod, we would see a much more boring 404 page with no error or stack trace details. We won't talk about it, but the Symfony docs have details about how you can customize the look and feel of your error pages on production.

The second thing I want to say is that the message - no question found for slug - is something that only developers will see. Feel free to make this as descriptive as you want: you don't need to worry about a real user seeing it.

Now that we have a Question object in our controller, let's use it in our template to render real, dynamic info. That's next.

Leave a comment!

13
Login or Register to join the conversation
Sherri Avatar

For anyone interested in getting a custom 404 or other error page.... I wrote a blog post about it:

https://blog.syntaxseed.com...

1 Reply

Hey Sherri,

Thank you for sharing your knowledge with others :)

Cheers!

Reply
quynh-ngan_vu Avatar
quynh-ngan_vu Avatar quynh-ngan_vu | posted 2 years ago

I follow the steps in this video. But it didn't auto complete methods in my code (01:45). What is the possibly reason for this?

1 Reply

Hey Ngan-Cat,

It depends if you have Symfony Plugin installed or no. In this tutorial Ryan has it installed and enabled, that's why he get that autocomplete. You can take a look at this video to know how to install and enable it: https://symfonycasts.com/sc...

Otherwise, you can hint your IDE about what class is that object by using @var annotation above the var, e.g.:


/** @var \App\Repository\QuestionRepository $repository */
$repository = $entityManager->getRepository(Question::class);
$repository-> // now you should have autocomplete here :)

I hope this helps!

Cheers!

1 Reply
Gediminas N. Avatar
Gediminas N. Avatar Gediminas N. | victor | posted 2 years ago

How to know which library should I use? Like you used your "\App\Repository\QuestionRepository $repository" seems like out of nowhere..

Reply

Hey fUb,

Well, it depends on what repository you need. If you call "getRepository(Question::class)", then I know that the repository behind of that Question entity is QuestionRepository. If you're not sure - you can try to dump the $repository var, e.g. with "dd($repository)" - it will give you a hint about instance of what class is that var.

Cheers!

Reply
skocdopolet Avatar
skocdopolet Avatar skocdopolet | posted 7 months ago

Hello,

I am trying to fetching data from repository and I found more solutions.

In this course, you are using entityManagerInterface

In official symfony docummentation,
https://symfony.com/doc/cur... is managerRegistry used

And I found another one objectManager...

Could you explain me the differrence between each one? Are there any other options I haven't mentioned?

Cheers, Thomas

Reply

Hey Tomas,

Good qustion! Well, ManagerRegistry is something more global in Doctrine, it will allow you to get instance to the entity manager iteself, to other Doctrine features. You can look at that service to see what you can do / get with it. With the EntityManagerInterface you only get the part of Doctrine features, i.e. the actual entity manager only. So, if you don't need the whole Doctrine features and only need the entity manager to be able to persist() and flush() data for exmaple - better to inject EntityManagerInterface, as it
s more precise dependency. If you even don't need those persit() and flush() methods - perfect, you probably even don't need an entity manager at all. Maybe you just need an entity repository? Inject the repo you need, e.g. UserRepository in case you need to get access to User entity data. The more specific services you inject - the better. So, it really depends on your specific case.

About ObjectManager - that's too low-level thing usually, mostly used in case to make unit tests easier to set up because it's easier to mock up it.

In short, depending on what you really need to get in your methods - inject the specific services that give you specific features you need, don't need to inject the global objects that gives you literally everything while you're using only a tiny part of it. This will help you when you will be testing your code as well, it would be easier to mock up :)

I hope it clarifies thing for you a bit!

Cheers!

Reply
m3tal Avatar

Hi.
How can I use the
findBy([
'isPaid' => not is null,
'isPrinted'=>null
]);

to get the not nullable values.
isPaid is a DateTime field, is not boolean.

thank you.

Reply

Hey m3tal

You can pass a DateTime object as a parameter if the field is configured as a Datime or Date. In your case it may be better to write a custom query instead of using the generic `findBy()` method

Cheers!

Reply
m3tal Avatar
m3tal Avatar m3tal | MolloKhan | posted 1 year ago | edited

Hi MolloKhan,
thanks for taking your time.
Finally I have opted for this road of creating a custom building because find method doesnt satisfy my needs.
thank you very much.

1 Reply

error message pleaaase =>Environment variable not found: "SENTRY_DSN".

Reply

Hey there,

It seems to me you forgot to define that environment variable, just add it to your .env or .env.local file

Cheers!

Reply
Cat in space

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

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.4.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^2.1", // 2.1.1
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
        "doctrine/orm": "^2.7", // 2.8.2
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "knplabs/knp-time-bundle": "^1.11", // v1.16.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "sentry/sentry-symfony": "^4.0", // 4.0.3
        "stof/doctrine-extensions-bundle": "^1.4", // v1.5.0
        "symfony/asset": "5.1.*", // v5.1.2
        "symfony/console": "5.1.*", // v5.1.2
        "symfony/dotenv": "5.1.*", // v5.1.2
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.1.*", // v5.1.2
        "symfony/monolog-bundle": "^3.0", // v3.5.0
        "symfony/stopwatch": "5.1.*", // v5.1.2
        "symfony/twig-bundle": "5.1.*", // v5.1.2
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.1.*", // v5.1.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.0.4
        "twig/twig": "^2.12|^3.0" // v3.0.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
        "symfony/debug-bundle": "5.1.*", // v5.1.2
        "symfony/maker-bundle": "^1.15", // v1.23.0
        "symfony/var-dumper": "5.1.*", // v5.1.2
        "symfony/web-profiler-bundle": "5.1.*", // v5.1.2
        "zenstruck/foundry": "^1.1" // v1.5.0
    }
}