php - Laravel 12 relation MorphTo with unknown type value - Stack Overflow

I'm trying to use Polymorphic Relationships from this docs .xeloquent-relationships#polymorphic-r

I'm trying to use Polymorphic Relationships from this docs .x/eloquent-relationships#polymorphic-relationships

I have model Balance

class Balance
{
    public $subject;
    public $subject_id;
    
    public function subjectable(): MorphTo
    {
        return $this->morphTo(__FUNCTION__, 'subject', 'subject_id');
    }

}

Also I have simple mapping in AppServiceProvider

        Relation::morphMap([
            'shop' => 'App\Models\Shop',
            'merchant' => 'App\Models\Merchant',
        ]);

So if the field subject contains 'shop' or 'merchant' - it works perfect.

But database is external resource, it's not strict like the code, and there might be some strings, that is not in my models and my app structure. And I can't control database and change it.

If subject contains string 'abc' , I'm getting an error: Class "abc" not found.

So it's impossible to use ->morphTo() in this case.

Is there a way to limit some "allowed list" of subjects (types), and return null or empty collection, if this subject not in my list?

UPD: I can't make check $this->subject inside relation function, because it's null.

    public function subjectable() {
        var_dump($this->subject);
        if (in_array($this->subject, ['shop', 'merchant'])) {
            return $this->morphTo(__FUNCTION__, 'subject', 'subject_id');
        }
        return null;
    }

I'm trying:

$result = $query->with(['subjectable'])->get(); // $this->subject is null
// or
$result = $query->get(); 
$result->load(['subjectable']); // $this->subject is null

I'm trying to use Polymorphic Relationships from this docs https://laravel/docs/12.x/eloquent-relationships#polymorphic-relationships

I have model Balance

class Balance
{
    public $subject;
    public $subject_id;
    
    public function subjectable(): MorphTo
    {
        return $this->morphTo(__FUNCTION__, 'subject', 'subject_id');
    }

}

Also I have simple mapping in AppServiceProvider

        Relation::morphMap([
            'shop' => 'App\Models\Shop',
            'merchant' => 'App\Models\Merchant',
        ]);

So if the field subject contains 'shop' or 'merchant' - it works perfect.

But database is external resource, it's not strict like the code, and there might be some strings, that is not in my models and my app structure. And I can't control database and change it.

If subject contains string 'abc' , I'm getting an error: Class "abc" not found.

So it's impossible to use ->morphTo() in this case.

Is there a way to limit some "allowed list" of subjects (types), and return null or empty collection, if this subject not in my list?

UPD: I can't make check $this->subject inside relation function, because it's null.

    public function subjectable() {
        var_dump($this->subject);
        if (in_array($this->subject, ['shop', 'merchant'])) {
            return $this->morphTo(__FUNCTION__, 'subject', 'subject_id');
        }
        return null;
    }

I'm trying:

$result = $query->with(['subjectable'])->get(); // $this->subject is null
// or
$result = $query->get(); 
$result->load(['subjectable']); // $this->subject is null
Share Improve this question edited Mar 4 at 15:55 Levsha asked Mar 4 at 11:12 LevshaLevsha 5364 silver badges19 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 2

You can create a dummy model that will be used for all types of data for which you do not have models, let's call it DummyModel.

You will need to extend Laravel's MorphTo model with your own version of the createModelByType method:

public function createModelByType($type)
{
    $class = Arr::get(Relation::morphMap() ?: [], $type, DummyClass::class);

    return tap(new $class, function ($instance) {
        if (! $instance->getConnectionName()) {
            $instance->setConnection($this->getConnection()->getName());
        }
    });
}

(The Laravel original version of this method has $class = Model::getActualClassNameForMorph($type);, and there is no easy way to replace the static method on Model.)

This will return your DummyClass any time the morph map does not have a matching class.

So the answer is complicated.

Like @Moshe Katz offered I created DummyClass and changed createModelByType().

  1. All my models extend CommonModel class, like Balance
class Balance extends CommonModel
{
    public $subject;
    public $subject_id;
    
    public function subjectable(): MorphTo
    {
        return $this->morphTo(__FUNCTION__, 'subject', 'subject_id');
    }
}
  1. In CommonModel I have overwrite method newMorphTo with my class App\Models\Replace\MorphTo
<?php
namespace App\Models;

use App\Models\Replace\MorphTo;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class CommonModel extends Model
{
    public function isDummy()
    {
        return false;
    }

    protected function newMorphTo(Builder $query, Model $parent, $foreignKey, $ownerKey, $type, $relation)
    {
        return new MorphTo($query, $parent, $foreignKey, $ownerKey, $type, $relation);
    }
}
  1. I have created app\Models\Replace\MorphTo.php class which return DummyClass:
<?php

namespace App\Models\Replace;

use App\Models\DummyClass;
use Illuminate\Database\Eloquent\Relations\MorphTo as IlluminateMorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Arr;

class MorphTo extends IlluminateMorphTo
{
    public function createModelByType($type)
    {
        $class = Arr::get(Relation::morphMap() ?: [], $type, DummyClass::class);

        return tap(new $class, function ($instance) {
            if (! $instance->getConnectionName()) {
                $instance->setConnection($this->getConnection()->getName());
            }
        });
    }
}
  1. Next we need DummyClass, that uses fake database.
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DummyClass extends Model
{
    protected $connection = 'sqlite_fake';

    public function isDummy()
    {
        return true;
    }
}
  1. Also we need to create that fake database in config/database.php
    'connections' => [

        'sqlite_fake' => [
            'driver' => 'sqlite',
            'database' => ':memory:',
            'prefix' => '',
        ],
    ...
  1. And finnaly we need to create empty table in memory. I put this code in AppServiceProvider boot() method.
        Schema::connection('sqlite_fake')->create('dummy_classes', function ($table) {
            $table->id();
        });

Can't you just do something like

    public function subjectable(): ?MorphTo
    {

        if (in_array($this->subject, ['shop', 'merchant'])) {
            return $this->morphTo(__FUNCTION__, 'subject', 'subject_id');
        }

        return null;
    }

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745047841a4608203.html

相关推荐

  • php - Laravel 12 relation MorphTo with unknown type value - Stack Overflow

    I'm trying to use Polymorphic Relationships from this docs .xeloquent-relationships#polymorphic-r

    7小时前
    50

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信