aymanalhattami/laravel-approval

Attach approvals to any model to prevent unauthorised updates.

1.0.0 2023-11-02 09:39 UTC

This package is auto-updated.

Last update: 2024-05-28 18:54:39 UTC


README

laravel-approval-banner.png
Attach modification approvals to any model to prevent unauthorised updates.

Note: This package is forked from cloudcake/laravel-approval in order to support latest laravel version and to add new features

Getting Started

Install the package via composer

composer require aymanalhattami/laravel-approval

Register the service provider

This package makes use of Laravel's auto-discovery. If you are an using earlier version of Laravel (< 5.4) you will need to manually register the service provider.

Add Approval\ApprovalServiceProvider::class to the providers array in config/app.php.

Publish configuration

php artisan vendor:publish --provider="Approval\ApprovalServiceProvider" --tag="config"

Publish migrations

php artisan vendor:publish --provider="Approval\ApprovalServiceProvider" --tag="migrations"

Run migrations

php artisan migrate

Setting Up

Setup Approval Model(s)

Any model you wish to attach to an approval process simply requires the RequiresApproval trait, for example:

use Approval\Traits\RequiresApproval;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use RequiresApproval;
}

Conditional Approvals

There may be instances where you don't always want your model to go through an approval process, for this reason the the requiresApprovalWhen is available for your convenience and defaults to false so that no unintended approvals are created behind the scenes:

/**
* Function that defines the rule of when an approval process
* should be actioned for this model.
*
* @param array $modifications
*
* @return boolean
*/
protected function requiresApprovalWhen(array $modifications) : bool
{
    // Handle some logic that determines if this change requires approval
    //
    // Return true if the model requires approval, return false if it
    // should update immediately without approval.
    return false;
}

Optional Attributes

Approval models come with a few optional attributes to make your approval process as flexible as possible. The following attributes are define by default with the set defaults, you may alter them per model as you please.

/**
* Number of approvers this model requires in order
* to mark the modifications as accepted.
*
* @var integer
*/
protected $approversRequired = 1;

/**
* Number of disapprovers this model requires in order
* to mark the modifications as rejected.
*
* @var integer
*/
protected $disapproversRequired = 1;

/**
* Boolean to mark whether or not this model should be updated
* automatically upon receiving the required number of approvals.
*
* @var boolean
*/
protected $updateWhenApproved = true;

/**
* Boolean to mark whether or not the approval model should be deleted
* automatically when the approval is disapproved wtih the required number
* of disapprovals.
*
* @var boolean
*/
protected $deleteWhenDisapproved = false;

/**
* Boolean to mark whether or not the approval model should be deleted
* automatically when the approval is approved wtih the required number
* of approvals.
*
* @var boolean
*/
protected $deleteWhenApproved = true;

Setup Approver Model(s)

Any other model (not just a user model) can approve models by simply adding the ApprovesChanges trait to it, for example:

use Approval\Traits\ApprovesChanges;
use Illuminate\Database\Eloquent\Model;

class Admin extends Model
{
    use ApprovesChanges;
}

Any model with the ApprovesChanges trait inherits the approval access function.

Approver Authorization (Optional)

By default, any model with the ApprovesChanges trait will be able to approve and disapprove modifications. You can customize your authorization to approve/disapprove modifications however you please by adding the authorizedToApprove method on the specific approver model:

use Approval\Traits\ApprovesChanges;
use Illuminate\Database\Eloquent\Model;

class Admin extends Model
{
    use ApprovesChanges;

    protected function authorizedToApprove(\Approval\Models\Modification $mod) : bool
    {
        // Return true to authorize approval, false to deny
        return true;
    }
}

Disapprover Authorization (Optional)

Similarly to the approval process, the disapproval authorization method for disapproving modifications follows the same logic:

use Approval\Traits\ApprovesChanges;
use Illuminate\Database\Eloquent\Model;

class Admin extends Model
{
    use ApprovesChanges;

    protected function authorizedToApprove(\Approval\Models\Modification $mod) : bool
    {
        // Return true to authorize approval, false to deny
        return true;
    }

    protected function authorizedToDisapprove(\Approval\Models\Modification $mod) : bool
    {
        // Return true to authorize disapproval, false to deny
        return true;
    }
}

Usage

Retrieving Pending Modifications

Any model that contains the RequiresApproval trait may have multiple pending modifications, to access these modifications you can call the modifications() method on the approval model:

$post = Post::find(1);
$post->modifications()->get();

Retrieving Only Pending Creations

$post = Post::find(1);
$post->modifications()->creations()->get();

Retrieving Only Pending Changes

$post = Post::find(1);
$post->modifications()->changed()->get();

Retrieving Modification Creator

For any pending modifications on a model, you may fetch the model that initiated the modification request:

$post = Post::find(1);
$post->modifications()->first()->modifier();

This (modifier) would usually be a user that changed the model and triggered the approval modification, but because Approval caters for more than just users, it's possible that the creator is any other model.

Retrieving A Model's Modifications

$active = Post::find(1)->modifications()->activeOnly()->get();
$inactive = Post::find(1)->modifications()->inactiveOnly()->get();
$any = Post::find(1)->modifications()->get();

Adding a Modification Approval

$modification = Post::find(1)->modifications()->first();
$reason = "This is optional reason.";

$approver = Admin::first();
$approver->approve($modification, $reason);

Adding a Modification Disapproval

$modification = Post::find(1)->modifications()->first();
$reason = "This is optional reason.";

$approver = Admin::first();
$approver->disapprove($modification, $reason);

Retrieving Modification Approvals

$post         = Post::find(1);
$modification = $post->modifications()->first();
$approval     = $modification->approvals()->get();

Retrieving Approval Author

$post         = Post::find(1);
$modification = $post->modifications()->first();
$approval     = $modification->approvals()->get();
$author       = $approval->approver();

Retrieving Approval Reason

$post         = Post::find(1);
$modification = $post->modifications()->first();
$approval     = $modification->approvals()->first();
$reason       = $approval->reason;

Retrieving Modification Disapprovals

$post         = Post::find(1);
$modification = $post->modifications()->first();
$approval     = $modification->disapprovals()->get();

Retrieving Disapproval Author

$post         = Post::find(1);
$modification = $post->modifications()->first();
$approval     = $modification->disapprovals()->get();
$author       = $approval->disapprover();

Retrieving disapproval Reason

$post         = Post::find(1);
$modification = $post->modifications()->first();
$approval     = $modification->disapprovals()->first();
$reason       = $approval->reason;

Retrieving Remaining Required Modification Approvals

$post         = Post::find(1);
$modification = $post->modifications()->first();
$remaining    = $modification->approversRemaining;

Retrieving Remaining Required Modification Disapprovals

$post         = Post::find(1);
$modification = $post->modifications()->first();
$remaining    = $modification->disapproversRemaining;

Forcing Modification Updates

$post         = Post::find(1);
$modification = $post->modifications()->first();
$modification->forceApprovalUpdate();