Salut à vous,

J'aurais une question. Actuellement je travaille sur un système de notifications, et pour cela j'utilise une relation polymorphique, car les notifications peuvent faire références à toutes sortes d'actions (réponse à un commentaire, "j'aime" sur un commentaire, etc). Le soucis du coup c'est que les relations polymorphiques obligent (ou alors je ne sais pas comment contourner ce problème) à une requête pour récupérer les données.

Sachant aussi que les "j'aimes" font aussi l'objet d'une relation polymorphique, vu qu'on peut "aimer" plusieurs éléments sur le site.

Du coup lorsque je me retrouve à faire un affichage comme ceci :

$notificable->article->slug
$notif->notificable->article->category->slug
// ou
$notificable->likeable->article->slug
$notificable->likeable->article->category->slug

Je me retrouve alors avec 4 requêtes générées fois 5 notifications du coup, ce qui n'est pas super optimisé. Voici comment je récupère mes notifications, et les relations qui en découlent :

public function getLastNotifications(User $userTo, $take = 5, $paginate = false)
{
        $query = $this->model
            ->with(['from' => function($query) {
                $query->select('id', 'pseudo');
            }])
            ->where('user_to', $userTo->id)
            ->latest();

        if( $paginate ) {
            return $query->paginate($take);
        } else {
            return $query->take($take)->get();
        }
 }
/**
     * Relation polymorphique
     *
     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
     */
    public function notificable()
    {
        return $this->morphTo();
    }

Ce que je veux

En fait j'aimerais diviser mon nombre de requêtes par 2 (ou 4 dans l'optimum). Par exemple, la liste des 5 dernières notifications

Imaginons qu'il y a deux notifications pour des "j'aimes" et 3 notifications pour une réponse à un commentaire, je me retrouve donc avec :

Pour chaque "j'aime" :

  • Une requête pour aller chercher la relation polymorphique likeable
  • Une requête pour aller chercher le commentaire en question
  • Une requête pour aller chercher l'article contenant le commentaire (en remontant la relation)
  • Une requête pour aller le slug de la catégorie de l'article (pour les routes)

Pour chaque Réponse de commentaire :

  • Une requête pour aller chercher le commentaire
  • Une requête pour aller chercher l'article contenant le commentaire
  • Une requête pour aller chercher la catégorie de l'article (besoin pour les routes)

je me retrouve donc avec 20 requêtes en tout pour liste ces éléments, ce qui fait beaucoup. Laravel n'aurait pas un tour caché pour alléger tout ça ? Ou devrais-je faire autrement ?

Voici mon foreach listant les notifications si ça peut aider :

@foreach( $notifications as $notif )

   @if( $notif->type == 'comment' )
      <li><a href="{{ route('articles.show', ['cat' => $notif->notificable->article->category->slug, $notif->notificable->article->slug]) }}#{{ $notif->notificable_id }}">{{ $notif->from->pseudo }} a répondu à votre commentaire</a></li>
   @elseif ($notif->type == 'like' )
      @if( $notif->notificable->up )
          <li><a href="{{ route('articles.show', ['cat' => $notif->notificable->likeable->article->category->slug, 'slug' => $notif->notificable->likeable->article->slug]) }}#{{ $notif->notificable->likeable_id }}">{{ $notif->from->pseudo }} a aimé votre commentaire</a></li>
      @else
          <li><a href="{{ route('articles.show', ['cat' => $notif->notificable->likeable->likeable->article->category->slug, 'slug' => $notif->notificable->likeable->article->slug]) }}#{{ $notif->notificable->likeable_id }}">{{ $notif->from->pseudo }} n'a pas aimé votre commentaire</a></li>
      @endif
   @endif
@endforeach

Ce que j'obtiens

Et du coup j'obtiens forcément trop de requêtes.

select * from `notifications` where `user_to` = '34639' and `read` = '0' order by `created_at` desc limit 5
select `id`, `pseudo` from `users` where `users`.`deleted_at` is null and `users`.`id` in ('34813')
select * from `likes` where `likes`.`id` = '10' limit 1
select * from `comments` where `comments`.`deleted_at` is null and `comments`.`id` = '126648' limit 1
select * from `articles` where `articles`.`deleted_at` is null and `articles`.`id` = '13460' limit 1
select * from `categories` where `categories`.`deleted_at` is null and `categories`.`id` in ('1')
select * from `likes` where `likes`.`id` = '9' limit 1
select * from `comments` where `comments`.`deleted_at` is null and `comments`.`id` = '126657' limit 1
select * from `articles` where `articles`.`deleted_at` is null and `articles`.`id` = '13460' limit 1
select * from `categories` where `categories`.`deleted_at` is null and `categories`.`id` in ('1')
select * from `likes` where `likes`.`id` = '8' limit 1
select * from `comments` where `comments`.`deleted_at` is null and `comments`.`id` = '126658' limit 1
select * from `articles` where `articles`.`deleted_at` is null and `articles`.`id` = '13460' limit 1
select * from `categories` where `categories`.`deleted_at` is null and `categories`.`id` in ('1')
select * from `comments` where `comments`.`deleted_at` is null and `comments`.`id` = '126661' limit 1
select * from `articles` where `articles`.`deleted_at` is null and `articles`.`id` = '13460' limit 1
select * from `categories` where `categories`.`deleted_at` is null and `categories`.`id` in ('1')
select * from `comments` where `comments`.`deleted_at` is null and `comments`.`id` = '126660' limit 1
select * from `categories` where `categories`.`deleted_at` is null and `categories`.`id` in ('1')
select * from `comments` where `comments`.`deleted_at` is null and `comments`.`id` = '126

Alors je ne suis pas à la recherche d'une solution toute faîte, mais d'une piste sur comment mettre en place un cache, ou des techniques pour permettre de réduire le nombre de requêtes en trichant sur les relations ou je ne sais pas :/

Merci à vous =)

4 réponses


Hello,

Si tu utilise la méthode with dispo sur les requetes eloquent ?

Par exemple :

Model::with(['my_poly_relation','my_poly_relation.likes','my_poly_relation.comments']);

Ca ne changerais pas le nombre de requetes ?

Salut et merci de ta réponse,

Le soucis c'est que tu ne peux pas (il me semble) faire une relation de ce type avec les relations polymorphiques.

Personne n'aurait une idée ?

Salut à toi,

Alors je te confirme que de mon côté, ce système fonctionne.
J'ai un model User avec une relation poly avec Citizen et Company (qui contient les détails des users en fonction du type).

Et dans mes requetes Eloquent, je fait :

//Controller
User::whereTrucMuch('truc')->with('details')->get();

//Model User
public function details(){
    return $this->morphTo();
}