• Stars
    star
    100
  • Rank 339,353 (Top 7 %)
  • Language
    PHP
  • License
    MIT License
  • Created almost 8 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Eloquent model history tracking for Laravel

Test Status Coverage Status Total Downloads Latest Stable Version License

History

Eloquent model history tracking for Laravel (NOW WITH AUTOLOAD!)

Installation

Composer

Laravel 6.x and above

composer require panoscape/history

Laravel 5.6.x

composer require "panoscape/history:^1.0"

Service provider and alias

Only required for Laravel 5.6.x

NO NEED for version 2.0+ which is auto-loaded with Package Discovery

config/app.php

'providers' => [
    ...
    Panoscape\History\HistoryServiceProvider::class,
];
'aliases' => [
    ...
    'App\History' => Panoscape\History\History::class,
];

Migration

php artisan vendor:publish --provider="Panoscape\History\HistoryServiceProvider" --tag=migrations

Config

php artisan vendor:publish --provider="Panoscape\History\HistoryServiceProvider" --tag=config

Localization

php artisan vendor:publish --provider="Panoscape\History\HistoryServiceProvider" --tag=translations

Usage

Add HasOperations trait to user model.

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Panoscape\History\HasOperations;

class User extends Authenticatable
{
    use Notifiable, SoftDeletes, HasOperations;
}

Add HasHistories trait to the model that will be tracked.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Panoscape\History\HasHistories;

class Article extends Model
{
    use HasHistories;

    public function getModelLabel()
    {
        return $this->display_name;
    }
}

Remember that you'll need to implement the abstract getModelLabel method from the trait. This provides the model instance's name in histories.

Get histories of a model

$model->histories();
//or dynamic property
$model->histories;

Get operations of a user

$user->operations();
//or dynamic property
$user->operations;

Additional query conditions

Both histories and operations return Eloquent relationships which also serve as query builders. You can add further constraints by chaining conditions:

// get the lastest 10 records
$model->histories()->orderBy('performed_at', 'desc')->take(10)

// filter by user id
$model->histories()->where('user_id', 10010)

History

//get the associated model
$history->model();

//get the associated user
//the user is the authenticated user when the action is being performed
//it might be null if the history is performed unauthenticatedly
$history->user();
//check user existence
$history->hasUser();

//get the message
$history->message;

//get the meta(only available when it's an updating operation)
//the meta will be an array with the properties changing information
$history->meta;

//get the timestamp the action was performed at
$history->performed_at;

Example message

Created Project my_project
   โ”‚       โ”‚         โ”‚
   โ”‚       โ”‚         โ””โ”€โ”€โ”€โ”€โ”€ instance name(returned from `getModelLabel`)
   โ”‚       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ model name(class name or localized name)
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ event name(default or localized name)

Example meta

[
    ['key' => 'name', 'old' => 'myName', 'new' => 'myNewName'],
    ['key' => 'age', 'old' => 10, 'new' => 100],
    ...
]

Custom history

Besides the built in created/updating/deleting/restoring events, you may store custom history record with ModelChanged event.

use Panoscape\History\Events\ModelChanged;

...
//fire a model changed event
event(new ModelChanged($user, 'User roles updated', $user->roles()->pluck('id')->toArray()));

The ModelChanged constructor accepts two/three arguments. The first is the associated model instance; the second is the message; the third is optional, which is the meta(array);

Localization

You may localize the model's type name.

To do that, add the language line to the models array in the published language file, with the key being the class's base name in snake case.

Example language config

/*
|--------------------------------------------------------------------------
| Tracker Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used across application for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/

'created' => 'ๅˆ›ๅปบ:model:label',

'updating' => 'actualizar :model :label',

'deleting' => ':model :label lรถschen',

'restored' => ':model:labelใ‚’ๅพฉๅ…ƒ',

//you may added your own model name language line here
'models' => [
    'project' => '้กน็›ฎ',
    'component_template' => '็ป„ไปถๆจกๆฟ',
]

This will translate your model history into

ๅˆ›ๅปบ้กน็›ฎproject_001

Filters

You may set whitelist and blacklist in config file. Please follow the description guide in the published config file.

/*
|--------------------------------------------------------------
| Events whitelist
|--------------------------------------------------------------
|
| Events in this array will be recorded.
| Available events are: created, updating, deleting, restored
|
*/
'events_whitelist' => [
    'created', 'updating', 'deleting', 'restored',
],

/*
|--------------------------------------------------------------
| Attributes blacklist
|--------------------------------------------------------------
| 
| Please add the whole class names. Example: \App\User:class
| For each model, attributes in its respect array will NOT be recorded into meta when performing update operation.
|
*/
'attributes_blacklist' => [
    // \App\User::class => [
    //     'password'
    // ],
],

/*
|--------------------------------------------------------------
| User blacklist
|--------------------------------------------------------------
|
| Operations performed by users in this array will NOT be recorded.
| Please add the whole class names. Example: \App\User:class
| Use 'nobody' to bypass unauthenticated operations
|
*/
'user_blacklist' => [
    
],
/*
|--------------------------------------------------------------
| Enviroments blacklist
|--------------------------------------------------------------
|
| When application's environment is in the list, tracker will be disabled
|
*/
'env_blacklist' => [
    
],

Auth guards

If your users are using non-default auth guards, you might see all $history->hasUser() become false even though the history sources were generated by authenticated users.

To fix this, you'll need to enable custom auth guards scanning in config file:

/*
|--------------------------------------------------------------
| Enable auth guards scan
|--------------------------------------------------------------
|
| You only need to enable this if your users are using non-default auth guards.
| In that case, all tracked user operations will be anonymous.
|
| - Set to `true` to use a full scan mode: all auth guards will be checked. However this does not ensure guard priority.
| - Set to an array to scan only specific auth guards(in the given order). e.g. `['web', 'api', 'admin']`
|
*/
'auth_guards' => null

Custom meta

You can define your own method for meta data. By default for updating event meta consists of modified keys and for other events meta is null.

Just redefine the method getModelMeta for the trait.

Example:

class Article extends Model
{
    // if you want to use default trait method, you need to redeclare it with a new name
    use HasHistories {
        getModelMeta as protected traitGetModelMeta;
    };

    ...
    
    public function getModelMeta($event)
    {
        // using defaults for updating
        if($event == 'updating') return $this->traitGetModelMeta($event);
        // passing full model to meta
        // ['key1' => 'value1', 'key2' => 'value2', ...]
        else return $this;
    }
}

Known issues

  1. When updating a model, if its model label(attributes returned from getModelLabel) has been modified, the history message will use its new attributes, which might not be what you expect.
class Article extends Model
{
    use HasHistories;

    public function getModelLabel()
    {
        return $this->title;
    }
}
// original title is 'my title'
// modify title
$article->title = 'new title';
$article->save();
// the updating history message
// expect: Updating Article my title
// actual: Updating Article new title

A workaround

public function getModelLabel()
{
    return $this->getOriginal('title', $this->title);
}

More Repositories

1

alpinewine

Wine with xvfb/x11/noVNC over alpine in docker
Dockerfile
9
star
2

laravel-vuforia

Vuforia web service API for Laravel
PHP
7
star
3

wxdevtool

ๅพฎไฟกๅผ€ๅ‘่€…ๅทฅๅ…ทDocker็‰ˆ
Dockerfile
3
star
4

wxapp-helper

WeChat Mini App Helper VSCode Extension
TypeScript
3
star
5

vue-three

Three.js components for Vue.js
Vue
3
star
6

privileges

Privilege and Group control for Laravel
PHP
3
star
7

GeneralGame

A general game framework for unity3D
C#
2
star
8

node2api

A CLI plugin to generate client request SDK from nodejs server projects
TypeScript
2
star
9

tagemup

Tagged caching which supports various drivers
JavaScript
1
star
10

braindead

websocket cli client and replicator
JavaScript
1
star
11

react-native-baidu-face-detector

Baidu Face SDK for React Native
Objective-C
1
star
12

Remoting

A simple windows ipc system
C#
1
star
13

babel-plugin-transform-private-members

Transform private class member names to Symbol
JavaScript
1
star
14

remarks

Eloquent model like and dislike features for Laravel
PHP
1
star
15

HyperCard

A hyper tool for magic the gathering
C#
1
star
16

playtable

Virtual playable unofficial server docker image
Dockerfile
1
star
17

typolar-template

Template parser for Typolar
JavaScript
1
star
18

magicrawler

Magic the Gathering data crawler
1
star
19

HyperMTG

This is a hyper tool for mtg(magic the gathering). It provides you with online data grabbing, local storing, deck editing, image packing and even more.
C#
1
star
20

dockernps

NPS in docker
Dockerfile
1
star
21

laravel-semantic

A template for laravel project with semantic-ui
JavaScript
1
star
22

tracker

Eloquent operations tracking for Laravel
PHP
1
star
23

mockit-express

Http API mock router for express application
JavaScript
1
star
24

gosocks

Docker hosted alpine based shadowsocks with kcptun
Dockerfile
1
star
25

access

Role and Permission control for Laravel
PHP
1
star