Home > Software engineering >  Laravel - MorphTo relation returns null
Laravel - MorphTo relation returns null

Time:07-30

I have a polymorphic relationship between an Article model and 3 models (Company, Group & User) that sometimes returns null.

90% of the time, the articleable relation is perfectly OK and looks like this:

#relations: array:1 [▼
    "articleable" => App\Models\User {#1443 ▼
      #connection: "mysql"
      #table: "users"
      #primaryKey: "id"
      #keyType: "int"
       incrementing: false
      #with: array:2 [▶]
      #withCount: []
       preventsLazyLoading: false
      #perPage: 15
       exists: true
       wasRecentlyCreated: false
      #escapeWhenCastingToString: false
      #attributes: array:7 [▶]
      #original: array:7 [▶]
      #changes: []
      #casts: []
      #classCastCache: []
      #attributeCastCache: []
      #dates: array:1 [▶]
      #dateFormat: null
      #appends: array:1 [▶]
      #dispatchesEvents: []
      #observables: []
      #relations: array:2 [▶]
      #touches: []
       timestamps: true
      #hidden: array:2 [▶]
      #visible: []
      #fillable: []
      #guarded: array:1 [▶]
      #rememberTokenName: "remember_token"
    }
  ]

But sometimes (1 / 10 ~), the relation is null:

#relations: array:1 [▼
    "articleable" => null
  ]

However, my model's attribute seems perfectly fine:

  #attributes: array:8 [▼
    "id" => "e5db380f-9516-491f-bf5d-c13eb2978eac"
    "articleable_type" => "App\Models\User" ←
    "articleable_id" => "20e00040-477b-44c5-800f-629a2380afe3" ←
    "title" => "Between yourself and me.' 'That's the most."
    "content" => "Alice. 'And ever since that,' the Hatter went on, spreading out the verses the White Rabbit interrupted: 'UNimportant, your Majesty means, of course,' the Mock  ▶"
    "image_url" => "https://via.placeholder.com/640x480.png/005511?text=non"
    "created_at" => "2022-07-27 11:51:01"
    "updated_at" => "2022-07-27 11:51:01"
  ]

And my DB does have the record (JSON export from my users table):

[
  {
    "id": "20e00040-477b-44c5-800f-629a2380afe3",
    "email": "[email protected]",
    "email_verified_at": "2022-07-27 11:51:01",
    "password": "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
    "remember_token": "3gRso464ST",
    "created_at": "2022-07-27 11:51:01",
    "updated_at": "2022-07-27 11:51:01"
  }
]

My models look like this:

User:

  App\Models\User ..........................................
  Database ........................................... mysql
  Table .............................................. users

  Attributes ................................... type / cast
  id unique ..................................... string(36)
  email unique, fillable ....................... string(255)
  email_verified_at nullable, fillable . datetime / datetime
  password fillable, hidden .................... string(255)
  remember_token nullable, fillable, hidden .... string(100)
  created_at nullable, fillable ........ datetime / datetime
  updated_at nullable, fillable ........ datetime / datetime
  type appended .................................. attribute

  Relations ................................................
  profile HasOne .................... App\Models\UserProfile
  companies BelongsToMany ............... App\Models\Company
  groups BelongsToMany .................... App\Models\Group
  page MorphOne ............................ App\Models\Page
  articles MorphMany .................... App\Models\Article
  notifications MorphMany  Illuminate\Notifications\DatabaseNotification

Company:

  App\Models\Company .......................................
  Database ........................................... mysql
  Table .......................................... companies

  Attributes ................................... type / cast
  id unique ..................................... string(36)
  name fillable ................................. string(60)
  description nullable, fillable ............... text(65535)
  created_at nullable, fillable ........ datetime / datetime
  updated_at nullable, fillable ........ datetime / datetime
  type appended .................................. attribute

  Relations ................................................
  page MorphOne ............................ App\Models\Page
  articles MorphMany .................... App\Models\Article

Group:

  App\Models\Group .........................................
  Database ........................................... mysql
  Table ............................................. groups

  Attributes ................................... type / cast
  id unique ..................................... string(36)
  name fillable ................................. string(60)
  description nullable, fillable ............... text(65535)
  created_at nullable, fillable ........ datetime / datetime
  updated_at nullable, fillable ........ datetime / datetime
  type appended .................................. attribute

  Relations ................................................
  page MorphOne ............................ App\Models\Page
  articles MorphMany .................... App\Models\Article

These models use the following HasArticles Trait:

<?php

namespace App\Models\Traits;

use App\Models\Article;
use Illuminate\Database\Eloquent\Relations\MorphMany;

trait HasArticles
{
    /**
     * Return model's articles.
     *
     * @return MorphMany
     */
    public function articles(): MorphMany
    {
        return $this->morphMany(Article::class, 'articleable');
    }
}

Article

  App\Models\Article .......................................
  Database ........................................... mysql
  Table ........................................... articles

  Attributes ................................... type / cast
  id unique ..................................... string(36)
  articleable_type fillable .................... string(255)
  articleable_id fillable ....................... string(36)
  title fillable ................................ string(70)
  content fillable .................................... text
  image_url nullable, fillable ................. string(255)
  created_at nullable, fillable ........ datetime / datetime
  updated_at nullable, fillable ........ datetime / datetime

  Relations ................................................

The Article model has the following MorphTo relationship:

public function articleable(): MorphTo
{
    return $this->morphTo(__FUNCTION__, 'articleable_type', 'articleable_id');
}

My create_articles_table migration (up Schema):

Schema::create('articles', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->uuidMorphs('articleable');
    $table->string('title', '70');
    $table->longText('content');
    $table->string('image_url')->nullable();
    $table->timestamps();
});

I am debugging in a Controller using the following:

$articles = Article::query()
    ->with('articleable')
    ->inRandomOrder()
    ->first();

dd($articles, $articles->articleable?->toArray());

Am I missing something? Thanks for your help.

Env:

PHP: 8.1
Laravel: 9.22
Nginx
MariaDB 10.7.4

CodePudding user response:

The problem is that you need to separate the first() method from the with('articleable'), i have chek up and it looks like modify the query in some way. Instead of with() use the load after that you have the result.

$articles = Article::query()
    ->inRandomOrder()
    ->first();
$articles->load('articleable');// try to add a check if $articles return null

CodePudding user response:

Turns out the solution was to not use \Str::uuid() (which uses Ramsey\Uuid\Uuid::uuid4()) to create my models' UUID, but to use Ramsey\Uuid\Uuid::uuid6() instead. No more issues, everything works fine now.

  • Related