简体   繁体   中英

Impossible to access an attribute on a null variable

I'm currently developping an online learning website with Symfony for a school project. Courses (formations) have sections, and sections have lessons. I'm working on displaying lessons. When you click in the catalog of formations, you get a page with a sidebar menu showing all sections and lessons, which you can click on. The issue is that when you click on a lesson, you get an error: Impossible to access an attribute ("title") on a null variable.

I'm guessing there's an issue with passing values from the initial formation page, where everything works fine, but I can't figure out how to make them 'follow'. When clicking on the lesson link, I get the right route as defined in the Controller, but the error seems to come from the original page (formation.html.twig).

Code in FormationsController for both the formation page and lesson page:

#[Route('/formations/consulter-{id}', name: 'app_formations_see')]
    public function see($id): Response
    {
        $formation = $this->doctrine->getRepository(Formation::class)->findOneById($id);
        $section  = $this->doctrine->getRepository(Section::class)->findAll();
        $lesson = $this->doctrine->getRepository(Lesson::class)->findAll();
        return $this->render('formations/formation.html.twig', [
            'formation' => $formation,
            'sections' => $section,
            'lessons' => $lesson
        ]);
    }

     #[Route('/formations/consulter-lecon-{id}', name: 'app_formations_lesson')]
    public function seeLesson($id): Response
    {
        $lesson = $this->doctrine->getRepository(Lesson::class)->findOneById($id);
        return $this->render('formations/lesson.html.twig', [
            'lesson' => $lesson
        ]);
    }

In formation.html.twig:

{% extends 'base.html.twig' %}
{% block title %}{{ formation.title }}{% endblock %}
{% block content %}
    <div class="formationcontainer text-center">
        <nav class="flex-shrink-0flex-shrink-0 p-3 bg-white sidenav">
            <button class="btn btn-success" id="sidenav-btn" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-expanded="false" aria-controls="collapseOne">
                Sommaire
            </button>
            <div class="list-unstyled ps-0 ul-custom navbar-collapse collapse show" id="sidebarCollapse" aria-expanded="true">
                <li class="mb-1">
                    {% for section in formation.sections %}
                    <ul class="list-unstyled align-items-center rounded fw-normal">
                        <li>{{ section.name }}</li>
                    </ul>
                    <div>
                        {% for lesson in section.lessons %}
                        <ul class="list-unstyled fw-normal pb-1 small">
                            <li><a href="{{ path('app_formations_lesson', {'id':lesson.id}) }}" class="link-dark rounded">{{ lesson.title }}</a></li>
                        </ul>
                        {% endfor %}
                    </div>
                    {% endfor %}
                </li>
                <li class="border-top my-3"></li>
                <li class="mb-1">
                    <ul class="list-unstyled fw-normal pb-1 small">
                        <li><a href="{{path('app_formations')}}" class="link-dark rounded">retour à la liste des formations</a></li>
                    </ul>
                </li>
            </div>
        </nav>
        <h1>{{ formation.title }} par {{ formation.user.firstname }} {{ formation.user.lastname }}</h1>
        {{ formation.description }}
        <hr>
        <h2>Sommaire</h2>
        <div class="tableau">
            <table class="table">
                {% for section in formation.sections %}
                    <thead class="table-success">
                    <tr>
                        <th scope="col">{{ section.name }}</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for lesson in section.lessons %}
                        <tr>
                            <td>{{ lesson.title }}</td>
                        </tr>
                    {% endfor %}
                    </tbody>
                {% endfor %}
            </table>
        </div>
    </div>
{% endblock %}

lesson.html.twig is basically the same code but the content changes and displays the lesson content instead of a summary of all sections and lessons

edit: added lesson.html.twig

{% extends 'base.html.twig' %}
{% block title %}titre de la leçon{% endblock %}
{% block content %}
    <div class="formationcontainer text-center">
        <nav class="flex-shrink-0flex-shrink-0 p-3 bg-white sidenav">
            <button class="btn btn-success" id="sidenav-btn" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-expanded="false" aria-controls="collapseOne">
                Sommaire
            </button>
            <div class="list-unstyled ps-0 ul-custom navbar-collapse collapse show" id="sidebarCollapse" aria-expanded="true">
                <li class="mb-1">
                    {% for section in formation.sections %}
                        <ul class="list-unstyled align-items-center rounded fw-normal">
                            <li>{{ section.name }}</li>
                        </ul>
                        <div>
                            {% for lesson in section.lessons %}
                                <ul class="list-unstyled fw-normal pb-1 small">
                                    <li><a href="#" class="link-dark rounded">{{ lesson.title }}</a></li>
                                </ul>
                            {% endfor %}
                        </div>
                    {% endfor %}
                </li>
                <li class="border-top my-3"></li>
                <li class="mb-1">
                    <ul class="list-unstyled fw-normal pb-1 small">
                        <li><a href="{{path('app_formations')}}" class="link-dark rounded">retour à la liste des formations</a></li>
                    </ul>
                </li>
            </div>
        </nav>
        <h1>Nom de la leçon</h1>
        <hr>
        <h2>Vidéo</h2>
        <h2>Contenu</h2>
    </div>
{% endblock %}

I experienced a similar problem, and eventually discovered the culprit after hours and hours. It's a tricky problem when you are sure there is data, so you are sure there is no way the variable should be null.

In my case, the link was actually going to a different route, not the intended route. Then the route was being taken as the {id}, therefore there was not be a record with such an id. This resulted to no record found thus the null.

How was the link going to an untargeted route? I had them like this:

Route 1:

#[Route('/formations/{id}', name: 'app_formations_see')]

Route 2:

#[Route('/formations/consulter', name: 'app_formations_see')]

So every time I hit a link to go to route 2(/formations/consulter), it would go to route 1(/formations/{id}) instead, which came before route 2 in the code, therefore found first. The problem was that 'consulter' was then being taken as the {id} for the route 1(/formations/{id}) when the link wrongfully goes to route 1 instead of route 2. In the database you won't find a formations record having id 'consulter', therefore the reason for the null variable.

This fails:

$formation = $this->doctrine->getRepository(Formation::class)->findOneById($id);

Because the ($id) will be substituted as:

$formation = $this->doctrine->getRepository(Formation::class)->findOneById(consulter);

Eventually I ensured there was no other route whose position was matching directly with {id}, or that was coming after the {id} route. For example, route 2 comes before route 1. Or I add an extra step to route 1 so that for example:

Route 1:

#[Route('/formations/forms/{id}', name: 'app_formations_see')]

Route 2:

#[Route('/formations/consulter', name: 'app_formations_see')]

Otherwise you can add a check for the null variable in your twig template :

Following straightupcraft's article:

{# Null Test The null test will tell you if a variable is null but can give you undesirable results when testing empty arrays or objects, as they will be considered not null #} {% if variable is not null %} ... {% endif %}

{# Empty Test Testing if a variable is empty is helpful if you don't know what type of value your variable might have. The empty test evaluates to true if the foo variable is null, false, an empty array, or an empty string #} {% if variable is not empty %} ... {% endif %}

But this means if you still go to the wrong route, yes the error will be handled and you may have a useful message displayed, but then the app will not work since it will be missing the data it should be rendering.

So I would suggest you work on the route structuring as explained at the top to avoid routes conflicts emanating from the {id} part

Test this by first renaming the twig template that is being rendered so that it's not found. The resulting "unable to find template" error will pinpoint in the controller, the erroneous line within the code block for the route your link is actually going to. Therefore verifying whether going to the wrong route

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM