برنامه نویسی

نحوه فعال کردن اتوآلود رابطه در نسخه ها قبل از v12.8

با بارگذاری خودکار-بارگذاری-باربری

چند روز پیش ، لیتوینچوک روش را اضافه کرد withRelationshipAutoloading در PR #53655. همانطور که در توضیحات ادغام توضیح داده شده است ، روابطی را که تماس می گیرید و اعمال می شود ، بررسی می کند بارگذاری خودکار به آنها البته این روش فقط در V12.8 وجود دارد ، بنابراین تصمیم گرفتم در صورت کار روی یک پروژه که قبل از آن از نسخه استفاده می کند ، آن را به نسخه های قبلی اضافه کنم. 🔥

بیایید قدم به قدم برویم تا بتوانم دقیقاً به شما نشان دهم که چگونه این کار را کردم. 👣

اول ، ما قصد داریم چند کلاس Laravel را بازنویسی کنیم ، مانند مدلبا مجموعه فصیحوت سازنده فصیح، همراه با برخی از صفات.

ما یک پوشه جدید به نام ایجاد خواهیم کرد رونوشت داخل فهرست برنامه. در داخل آن ، ما دو زیر پوشه خواهیم داشت: یکی به نام کلاس و دیگری نامیده می شود ویژگیبشر

بنابراین ، بیایید با صفات شروع کنیم. ما یک مورد جدید به نام ایجاد خواهیم کرد hasattributesبشر این ویژگی در حال حاضر در لاراول وجود دارد ، اما ما می خواهیم نسخه خود را از آن در پروژه با همین نام ایجاد کنیم (اگرچه می توانید آن را هر چیزی که دوست دارید نامگذاری کنید). در اینجا کدی که ما در آن قرار خواهیم داد:

namespace App\Overwrites\Traits;

trait HasAttributes
{
    /**
     * Get a relationship.
     *
     * @param  string  $key
     * @return mixed
     */
    public function getRelationValue($key)
    {
        // If the key already exists in the relationships array, it just means the
        // relationship has already been loaded, so we'll just return it out of
        // here because there is no need to query within the relations twice.
        if ($this->relationLoaded($key)) {
            return $this->relations[$key];
        }

        if (! $this->isRelation($key)) {
            return;
        }

        if ($this->attemptToAutoloadRelation($key)) {
            return $this->relations[$key];
        }

        if ($this->preventsLazyLoading) {
            $this->handleLazyLoadingViolation($key);
        }

        // If the "attribute" exists as a method on the model, we will just assume
        // it is a relationship and will load and return results from the query
        // and hydrate the relationship's value on the "relationships" array.
        return $this->getRelationshipFromMethod($key);
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما همچنین قصد داریم ویژگی دیگری به نام ایجاد کنیم جنجال، و در داخل آن ، کد زیر را اضافه خواهیم کرد:


namespace App\Overwrites\Traits;

use Closure;
use Illuminate\Database\Eloquent\Model;

trait HasRelationships
{
    /**
     * The relationship autoloader callback.
     *
     * @var \Closure|null
     */
    protected $relationAutoloadCallback = null;

    /**
     * Determine if a relationship autoloader callback has been defined.
     *
     * @return bool
     */
    public function hasRelationAutoloadCallback()
    {
        return ! is_null($this->relationAutoloadCallback);
    }

    /**
     * Define an automatic relationship autoloader callback for this model and its relations.
     *
     * @param  \Closure  $callback
     * @param  mixed  $context
     * @return $this
     */
    public function autoloadRelationsUsing(Closure $callback, $context = null)
    {
        $this->relationAutoloadCallback = $callback;

        foreach ($this->relations as $key => $value) {
            $this->propagateRelationAutoloadCallbackToRelation($key, $value, $context);
        }

        return $this;
    }

    /**
     * Enable relationship autoloading for this model.
     *
     * @return $this
     */
    public function withRelationshipAutoloading()
    {
        $this->newCollection([$this])->withRelationshipAutoloading();

        return $this;
    }

    /**
     * Attempt to autoload the given relationship using the autoload callback.
     *
     * @param  string  $key
     * @return bool
     */
    protected function attemptToAutoloadRelation($key)
    {
        if (! $this->hasRelationAutoloadCallback()) {
            return false;
        }

        $this->invokeRelationAutoloadCallbackFor($key, []);

        return $this->relationLoaded($key);
    }

    /**
     * Invoke the relationship autoloader callback for the given relationships.
     *
     * @param  string  $key
     * @param  array  $tuples
     * @return void
     */
    protected function invokeRelationAutoloadCallbackFor($key, $tuples)
    {
        $tuples = array_merge([[$key, get_class($this)]], $tuples);

        call_user_func($this->relationAutoloadCallback, $tuples);
    }

    /**
     * Propagate the relationship autoloader callback to the given related models.
     *
     * @param  string  $key
     * @param  mixed  $values
     * @param  mixed  $context
     * @return void
     */
    protected function propagateRelationAutoloadCallbackToRelation($key, $models, $context = null)
    {
        if (! $this->hasRelationAutoloadCallback() || ! $models) {
            return;
        }

        if ($models instanceof Model) {

            $models = [$models];

        }

        if (! is_iterable($models)) {
            return;
        }

        $callback = fn(array $tuples) => $this->invokeRelationAutoloadCallbackFor($key, $tuples);

        foreach ($models as $model) {
            // Check if relation autoload contexts are different to avoid circular relation autoload...
            if (is_null($context) || $context !== $model) {
                $model->autoloadRelationsUsing($callback, $context);
            }
        }
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بعد ، ما سفارشی خود را ایجاد خواهیم کرد مدل کلاس. البته این کلاس گسترش خواهد یافت مدل پیش فرض لاراول بنابراین ما می توانیم تمام ویژگی های اصلی را حفظ کنیم. سپس ، کد زیر را در داخل آن اضافه خواهیم کرد:

use App\Overwrites\Traits\{HasAttributes, HasRelationships};
use Illuminate\Database\Eloquent\Model as BaseModel;

abstract class Model extends BaseModel
{
    use HasAttributes, HasRelationships;

    /**
     * Indicates whether relations should be automatically loaded on all models when they are accessed.
     *
     * @var bool
     */
    protected static $modelsShouldAutomaticallyEagerLoadRelationships = false;

    /**
     * Determine if model relationships should be automatically eager loaded when accessed.
     *
     * @param  bool  $value
     * @return void
     */
    public static function automaticallyEagerLoadRelationships($value = true)
    {
        static::$modelsShouldAutomaticallyEagerLoadRelationships = $value;
    }

    /**
     * Determine if relationships are being automatically eager loaded when accessed.
     *
     * @return bool
     */
    public static function isAutomaticallyEagerLoadingRelationships()
    {
        return static::$modelsShouldAutomaticallyEagerLoadRelationships;
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

همین مسئله برای سازنده کلاس – ما نسخه خود را ایجاد می کنیم و کد زیر را در داخل آن اضافه می کنیم:

namespace App\Overwrites\Classes;

use Illuminate\Database\Eloquent\Builder as BaseBuilder;

class Builder extends BaseBuilder
{
    /**
     * Execute the query as a "select" statement.
     *
     * @param  array|string  $columns
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function get($columns = ['*'])
    {
        $builder = $this->applyScopes();

        // If we actually found models we will also eager load any relationships that
        // have been specified as needing to be eager loaded, which will solve the
        // n+1 query issue for the developers to avoid running a lot of queries.
        if (count($models = $builder->getModels($columns)) > 0) {
            $models = $builder->eagerLoadRelations($models);
        }

        $collection = $builder->getModel()->newCollection($models);

        if (Model::isAutomaticallyEagerLoadingRelationships()) {
            $collection->withRelationshipAutoloading();
        }

        return $this->applyAfterQueryCallbacks($collection);
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

سرانجام ، برای مجموعه کلاس ، ما نسخه خود را ایجاد می کنیم و کد زیر را اضافه می کنیم:

namespace App\Overwrites\Classes;

use Illuminate\Database\Eloquent\Collection as BaseCollection;

class Collection extends BaseCollection
{
    /**
     * Load a relationship path for models of the given type if it is not already eager loaded.
     *
     * @param  array>  $tuples
     * @return void
     */
    public function loadMissingRelationshipChain(array $tuples)
    {
        [$relation, $class] = array_shift($tuples);

        $this->filter(function ($model) use ($relation, $class) {
            return ! is_null($model) &&
                ! $model->relationLoaded($relation) &&
                $model::class === $class;
        })->load($relation);

        if (empty($tuples)) {
            return;
        }

        $models = $this->pluck($relation)->whereNotNull();

        if ($models->first() instanceof BaseCollection) {
            $models = $models->collapse();
        }

        (new static($models))->loadMissingRelationshipChain($tuples);
    }

    /**
     * Enable relationship autoloading for all models in this collection.
     *
     * @return $this
     */
    public function withRelationshipAutoloading()
    {
        $callback = fn($tuples) => $this->loadMissingRelationshipChain($tuples);

        foreach ($this as $model) {
            if (! $model->hasRelationAutoloadCallback()) {
                $model->autoloadRelationsUsing($callback);
            }
        }

        return $this;
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

و اکنون ، مرحله آخر: شما مطمئن خواهید شد که تمام مدل های شما گسترش می یابد مدل سفارشی کلاس که قبلاً ایجاد کردیم. همچنین ، از لاراول استفاده کنید کلاسهای سفارشی ما ساختیم.

شما این کار را در هر مدلی انجام می دهید که می خواهید آن را فعال کنید بارگذاری خودکار، مانند این:

namespace App\Models;

use App\Overwrites\Classes\Collection as CustomCollection;
use App\Overwrites\Classes\Builder as CustomBuilder;
use App\Overwrites\Classes\Model as CustomModel;

class Post extends CustomModel
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'title',
        'body',
        'user_id',
        'is_visible',
        'views',
    ];

    /**
     * Create a new Eloquent query builder for the model.
     *
     * @param \Illuminate\Database\Query\Builder $query
     * @return \App\Overwrites\Builder
     */
    public function newEloquentBuilder($query)
    {
        return new CustomBuilder($query);
    }

    /**
     * Create a new Eloquent Collection instance.
     *
     * @param  array  $models
     * @return TCollection
     */
    public function newCollection(array $models = [])
    {
        return new CustomCollection($models);
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در اینجا نسخه عربی این مقاله آورده شده است. لذت بردن!

📥 برای اخبار اختصاصی ثبت نام کنید!
https://blog.mmramadan.com/newsletters

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا