نظام تخويل الموارد المتقدم في Laravel. الجزء 3. سمات القراءة / الكتابة ، النماذج الخاصة

المقدمة


مرحبا عزيزي خابروفشان.

أوجه انتباهكم اليوم إلى الجزء الأخير من سلسلة مقالات عن التفويض المتقدم للإجراءات باستخدام موارد في Laravel. من أجل فهم أفضل ما سيتم مناقشتها في هذه المقالة - تحتاج إلى قراءة الأولى و الثانية قطع الغيار.


هذه المرة ، تغير بيان المشكلة قليلاً: لدينا بالفعل تفويض للإجراءات مع الموارد عن طريق تفويض طرق وحدات التحكم المقابلة. وتتمثل المهمة في الإذن بتعديل / عرض حقول محددة للنموذج. تحتاج أيضًا إلى إدراك القدرة على تخويل التحرير / العرض بواسطة مؤلف النماذج الخاصة بك.


هذه المادة مخصصة لممارسي البرمجة ، وسيكون من الصعب على المطور المبتدئ فهمها.



الجزء 4. تخويل الإجراءات مع السمات


الجزء النظري


لتنفيذ هذه الوظيفة ، أخطط لاستخدام السمات المضمنة في Laravel HasAttributes و HidesAttributes و GuardsAttributes . باستخدام هذه السمات ، يتحكم الإطار في الملء الجماعي وعرض السمات. أخطط لترك القدرة على ملء / قراءة السمات بقوة ، لأنه إذا فوضت جميع إجراءات القراءة / الكتابة بالكامل ، فمن المحتمل جدًا أن نقاطع أداء البرنامج.


لإنجاز المهمة ، نحتاج إلى:


  • تجاوز أساليب getHidden () ، getVisible () لإخفاء الحقول غير المصرح بها.
  • تجاوز طريقة isFillable () للحماية من تسجيل الحقول غير المصرح به.
  • authUpdate() .
  • authView() .
  • totallyGuarded() .

— , , . . , . - Banana Monkey Jungle, . / — . .


Laravel :


  • $visible
  • $hidden
  • $guarded
  • $fillable

. $visible . — $guarded . — , .



// , . app/Extensions/Traits, app/Extensions/Traits/GuardedModel. , ( ).

authView()( ) authUpdate()( ), , .

getVisible() getHidden().

, . filterVisibility(), , () .

filterVisibility() $hidden makeHidden(), , userCanViewAttribute($key) . makeVisible()


isFillable($key), , userCanUpdateAttribute($key)


userCanUpdateAttribute($key) userCanViewAttribute($key) . $user->can() spatie/laravel-permission. , , .


GuardedModel


<?php

namespace App\Extensions\Traits;

use App\Models\User;
use Illuminate\Database\Eloquent\Model;

trait GuardedModel
{
    protected function authView(): array
    {
        return [];
    }

    protected function authUpdate(): array
    {
        return [];
    }

    public function getHidden(): array
    {
        $this->filterVisibility();

        return parent::getHidden();
    }

    public function getVisible(): array
    {
        $this->filterVisibility();

        return parent::getVisible();
    }

    public function isFillable($key)
    {
        if (in_array($key, $this->authView())) {
            return $this->userCanUpdateAttribute($key);
        }

        return parent::isFillable($key);
    }

    private function filterVisibility(): void
    {
        $this->makeHidden($this->authView());

        $authVisible = array_filter(
            $this->authView(),
            fn ($attr) => $this->userCanViewAttribute($attr)
        );

        $this->makeVisible($authVisible);
    }

    private function userCanViewAttribute(string $key): bool
    {
        /** @var User $user */
        $user = auth()->user();
        $ability = !empty($user) && $user->can("view-attr-$key-" . static::class);

        return $ability;
    }

    private function userCanUpdateAttribute(string $key): bool
    {
        /** @var User $user */
        $user = auth()->user();
        $ability = !empty($user) && $user->can("update-attr-$key-" . static::class);

        return $ability;
    }

    public function totallyGuarded()
    {
        $guarded = (
            count($this->getFillable()) === 0
            && count($this->authView()) === 0
            && $this->getGuarded() == ['*']
        );

        return  $guarded;
    }
}

totallyGuarded(). Illuminate\Database\Eloquent\MassAssignmentException , . — , .

$user->can(«update-attr-$key-». static::class), . , attr. /.


, authView() authUpdate(). user user_id. user_id, user user_id . . , , $guarded, $hidden, $fillable, .


Posts


<?php

namespace App\Models;

use App\Extensions\Traits\GuardedModel;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use GuardedModel;

    protected $hidden = ['id'];

    protected $guarded = ['created_at', 'updated_at'];

    protected $fillable = ['title', 'description', 'user_id'];

    protected function authView(): array
    {
        return ['user_id', 'user'];
    }

    protected function authUpdate(): array
    {
        return ['user_id'];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

5.



. user(), user_id. . , . $user->can('delete-self-'. $this->getModelClass()). , . isOwner(User $user, Model $model). . , , — .


السياسة العامة للنموذج


<?php

namespace App\Policies;

use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;

abstract class ModelPolicy
{
    use HandlesAuthorization;

    abstract protected function getModelClass(): string;

    public function delete(User $user, Model $model)
    {
        if ($user->can('delete-' . $this->getModelClass())) {
            return true;
        }

        if ($user->can('delete-self-' . $this->getModelClass())) {
            return $this->isOwner($user, $model);
        }

        return false;
    }

    private function isOwner(User $user, Model $model): bool
    {
        if (!empty($user) && method_exists($model, 'user')) {
            return $user->getKey() === $model->getRelation('user')->getKey();
        }

        return false;
    }
}

وألاحظ أيضًا أنه لتجنب الأخطاء في وقت التشغيل ، يجدر إدخال التحقق من توفر طريقة المستخدم () أو ما شابه ذلك في النموذج المستهدف.


هذا كل شئ حتى الان. آمل أن تكون هذه المقالة مفيدة. لدينا نظام كامل ومرن إلى حد ما لتفويض الإجراءات. إذا كان لديك أسئلة ، سأجيب بالتأكيد. وبالطبع ، نرحب أيضًا بالتعليقات أو الاقتراحات البناءة. إذا كنت ترغب في ذلك ، يمكنك مشاهدة تنفيذ هذا المشروع التدريبي بمزيد من التفاصيل في المستودع .


All Articles