Slim 4 with Eloquent model drill down in view

Hey all So i have a slim4 project and im using Eloquent. Each user is connected to multiple roles and i have the relationship setup.

use Illuminate\Database\Concerns\BuildsQueries;
use Illuminate\Database\Eloquent\Model;

class Users extends Model
{
    use BuildsQueries;

    protected $table = "users";
    
    protected $fillable = [
        "username",
        "first_name",
        "middle_name",
        "last_name",
        "email",
        "password",
        "flag_active",
        "active_hash",
        "recover_hash",
        "remember_identifier",
        "remember_token",
        "fk_user_type",
    ];

    public function getUserFulleName()
    {
        return $this->first_name . " " . $this->last_name;
    }

    public function getRoles()
    {
        return $this->hasMany(UserHasRoles::class, 'users_id', 'id');
    }

}

once the middleware kicks in it check if the user is authed and then assigned the user model to a view global

public function getUserDetails()
    {

        return Users::select(
                [
                    "username",
                    "first_name",
                    "middle_name",
                    "last_name",
                    "email"
                ]
            )
            ->find($this->session->get('userID'));
    }

however when i try and retrieve the roles in the view its not doing the actual get.

{{ dump(authUser.userDetails.getRoles) }}

and {{ dump(authUser.userDetails.getRoles()) }} just brings the queryObject back.

I tried the above with and withAll options but still nothing…

bump bump bump bump :slight_smile:

Have you found a solution?

I would guess that the session is not startet when you try to fetch data from an upper layer. I would try to implement it more “MVC friendly” and pass the data from the controller to the template engine instead.

Hi Oden thanks for coming to my rescue again :stuck_out_tongue: sorry i left out some code.

I have a auth middleware that kicks in once authorized:

class UserAuthenticationMiddleware extends Middleware
{

    /**
     * @param Request $request
     * @param RequestHandler $handler
     * @return Response
     */
    public function __invoke(Request $request, RequestHandler $handler): Response
    {

        /* @var Twig $view*/
        $view = $this->container->get("view");

        /* @var Helper $session*/
        $session = $this->container->get("session");

        if ($session->exists('userID')) {

            $view->getEnvironment()->addGlobal(
                'authUser',
                [
                    'userDetails' => $this->container->get('auth')->getUserDetails()
                ]
            );

            $view->getEnvironment()->addGlobal(
                'authUserPermissions',
                $session->get('userPermissions')
            );

            $view->getEnvironment()->addGlobal(
                'authorized',true
            );
        }

        return $handler->handle($request);
    }

}

that creates all my view veriables for twig and thats gets the below:

public function getUserDetails(): Users
    {
        return Users::select(
            [
                "id",
                "username",
                "first_name",
                "middle_name",
                "last_name",
                "email"
            ]
        )
            ->find($this->session->get('userID'));
    }

So my understanding is from calling auth.userdetails.getRoles in the view it should lazy load the roles the current user has?

and because a user can have multiple roles i should be able to use auth.userdetails.getRoles[key] to get each unique roles details correct?

I see at least two issues.

  • The Twig addGlobal works only as long as no other Twig template has been rendered, because after the first rendering twig blocks all changes to the global variables
  • I would refactor the getUserDetails method a little pit and pass the UserId from the session instead of fetching within the Model layer via the session. Otherwise it breaks MVC.

Example:

public function getUserDetails(int $userId): Users
{
    return Users::select(
        [
            "id",
            "username",
            "first_name",
            "middle_name",
            "last_name",
            "email"
        ]
    )->find($userId);
}

Middleware:

<?php

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Exception\HttpUnauthorizedException;
// ...

class UserAuthenticationMiddleware implements MiddlewareInterface
{
    // ...

    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface {
        /* @var Twig $view */
        $view = $this->container->get('view');

        /* @var Helper $session */
        $session = $this->container->get('session');

        $userId = $session->get('userID');

        if (!$userId) {
            throw new HttpUnauthorizedException();
        }

        $environment = $view->getEnvironment();

        $environment->addGlobal(
            'authUser',
            [
                'userDetails' => $this->container->get('auth')->getUserDetails($userId)
            ]
        );

        $environment->addGlobal('authUserPermissions', $session->get('userPermissions'));
        $environment->addGlobal('authorized', true);

        return $handler->handle($request);
    }
}

Thanks for the reply. I refactored as per above. Will this also fix my drill down issue?

My example Middleware should solve the (lazy loading) Session issue. I would try this first. If it works, then everything should be fine.

If you get a Twig error message like Unable to add global "variablename" as the runtime or the extensions have already been initialized. then you may try this workaround: Twig dynamic global variables.

In my experience the best way to handle the results of a query in eloquent returning an associative array.

On the other hand I would use the with to define the relationship, so that the query looks like this:

public function getUserDetails()
{
        return Users::with(['getRoles'])
                ->select(
                    [
                        "username",
                        "first_name",
                        "middle_name",
                        "last_name",
                        "email"
                    ]
                )
                ->find($this->session->get('userID'))
                ->get()
                ->toArray();
}

@pauhoms thanks for you input. Ill see about the conversion to array. Currently the model works well for me. I have been experience issue with using “with” option on the model not sure what I did but its working now in one of my api calls.