Laravel conditional array using Resource - laravel

I've got People table with columns: person, name, text.
column person can have only values like: physician, dentist, nurse, etc...
I want to send it via api using Laravel Resource, so I created basic PeopleResource.php:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class PeopleResource extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'name' => $this->name,
'text' => $this->text
];
}
}
this is simple and gives me an array of objects with name and text.
Now I want to make more complicated array that will give me something like this:
Please note that I want to get all person types into my array here. This is the problem of making my array structured - so I can access it like result.physician.name, result.nurse.name etc..
result: {
physician: [{name: "name1", text: "text2"}, {name: "name2", text: "text2"}...],
nurse: [{name: "name1", text: "text2"}, {name: "name2", text: "text2"}...],
dentist: [{name: "name1", text: "text2"}, {name: "name2", text: "text2"}...],
otherTypeOfPerson: [], ....
}
How can I do it using Laravel Resource?
edit: my controller
<?php
namespace App\Http\Controllers;
use App\People;
use Illuminate\Http\Request;
use App\Http\Resources\PeopleResource;
class PeoplesController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$peoples= People::orderBy('id','asc')->get();
return PeopleResource::collection($peoples);
}
edit2: I was thinking about this solution, but this seems not working:
public function toArray($request)
{
return [
if($this->person == 'nurse') {
'nurse' => [
'name' => $this->name,
'text' => $this->text
]
}
if($this->person == 'physician') {
'physician' => [
'name' => $this->name,
'text' => $this->text
]
}
];
}

$result = YourModel:groupBy('person')->get();
dd($result);
//define your field name in model

Use groupBy() on collection.
public function index()
{
$peoples = People::select(['name', 'text', 'person'])->orderBy('id','asc')->get();
return $peoples->groupBy('person')->toArray();
}

Related

Validating array - get current iteration

I'm trying to validate a POST request using Laravel's FormRequest.
The customer is submitting an order, which has an array of items. We are requiring the user to indicate whether the item needs special_delivery only if the asking_price > 500 and the quantity > 10.
The following are my intended rules:
public function rules() {
'customer_id' => 'required|integer|exists:customers,id',
'items' => 'required|array',
'items.*.name' => 'required|string',
'items.*.asking_price' => 'required|numeric',
'items.*.quantity' => 'required|numeric',
'items.*.special_delivery' // required if price > 500 && quantity > 10
}
I've attempted to do something along these lines:
Rule::requiredIf($this->input('item.*.asking_price') > 500 && $this->input('item.*.quantity' > 10));
The problem with this is that I can't find a way to access the current items iteration index to indicate which item to validate against.
I also tried the following custom validation:
function ($attribute, $value, $fail) {
preg_match('/\d+/', $attribute, $m);
$askingPrice = $this->input('items')[$m[0]]['asking_price'];
$quantity= $this->input('items')[$m[0]]['quantity'];
if ($askingPrice > 500 && $quantity > 10) {
$fail("$attribute is required");
}
}
Although this function gives me access to the current $attribute,the problem is that it will only run if special_delivery exists. Which defeats the entire purpose!
Any help will be much appreciated!
Thank you!
I might've come up with a solution to your problem, a index aware sometimes if you so will.
Since it's unfortunately not possible to add macros to the Validator, you would either have to override the validation factory (that's what I suggest) and use your own custom validation class or make a helper function based off the method, pass the Validator instance as an additional parameter and use this instead of $this.
Sauce first: the indexAwareSometimes validation function
function indexAwareSometimes(
\Illuminate\Contracts\Validation\Validator $validator,
string $parent,
$attribute,
$rules,
\Closure $callback
) {
foreach (Arr::get($validator->getData(), $parent) as $index => $item) {
if ($callback($validator->getData(), $index)) {
foreach ((array) $attribute as $key) {
$path = $parent.'.'.$index.'.'.$key;
$validator->addRules([$path => $rules]);
}
}
}
}
A lot of inspiration obviously came from the sometimes method and not much has changed. We're basically iterating through the array (the $parent array, in your case items) containing all our other arrays (items.*) with actual data to validate and adding the $rules (required) to $attribute (special_delivery) in the current index if $callback evaluates to true.
The callback closure requires two parameters, first being the form $data of your parent validation instance, retrieved by Validator::getData(), second the $index the outer foreach was at the time it called the callback.
In your case the usage of the function would look a little like this:
use Illuminate\Support\Arr;
class YourFormRequest extends FormRequest
{
public function rules()
{
return [
'customer_id' => 'required|integer|exists:customers,id',
'items' => 'required|array',
'items.*.name' => 'required|string',
'items.*.asking_price' => 'required|numeric',
'items.*.quantity' => 'required|numeric',
];
}
public function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
indexAwareSometimes(
$validator,
'items',
'special_delivery',
'required',
fn ($data, $index) => Arr::get($data, 'items.'.$index.'.asking_price') > 500 &&
Arr::get($data, 'items.'.$index.'.quantity') > 10
);
}
}
Extending the native Validator class
Extending Laravel's native Validator class isn't as hard as it sounds. We're creating a custom ValidationServiceProvider and inherit Laravel's Illuminate\Validation\ValidationServiceProvider as a parent. Only the registerValidationFactory method needs to be replaced by a copy of it where we specify our custom Validator resolver that should be used by the factory instead:
<?php
namespace App\Providers;
use App\Validation\CustomValidator;
use Illuminate\Contracts\Translation\Translator;
use Illuminate\Validation\Factory;
use Illuminate\Validation\ValidationServiceProvider as ParentValidationServiceProvider;
class ValidationServiceProvider extends ParentValidationServiceProvider
{
protected function registerValidationFactory(): void
{
$this->app->singleton('validator', function ($app) {
$validator = new Factory($app['translator'], $app);
$resolver = function (
Translator $translator,
array $data,
array $rules,
array $messages = [],
array $customAttributes = []
) {
return new CustomValidator($translator, $data, $rules, $messages, $customAttributes);
};
$validator->resolver($resolver);
if (isset($app['db'], $app['validation.presence'])) {
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
}
The custom validator inherits Laravel's Illuminate\Validation\Validator and adds the indexAwareSometimes method:
<?php
namespace App\Validation;
use Closure;
use Illuminate\Support\Arr;
use Illuminate\Validation\Validator;
class CustomValidator extends Validator
{
/**
* #param string $parent
* #param string|array $attribute
* #param string|array $rules
* #param Closure $callback
*/
public function indexAwareSometimes(string $parent, $attribute, $rules, Closure $callback)
{
foreach (Arr::get($this->data, $parent) as $index => $item) {
if ($callback($this->data, $index)) {
foreach ((array) $attribute as $key) {
$path = $parent.'.'.$index.'.'.$key;
$this->addRules([$path => $rules]);
}
}
}
}
}
Then we just need to replace Laravel's Illuminate\Validation\ValidationServiceProvider with your own custom service provider in config/app.php and you're good to go.
It even works with Barry vd. Heuvel's laravel-ide-helper package.
return [
'providers' => [
//Illuminate\Validation\ValidationServiceProvider::class,
App\Providers\ValidationServiceProvider::class,
]
]
Going back to the example above, you only need to change the getValidatorInstance() method of your form request:
public function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
$validator->indexAwareSometimes(
'items',
'special_delivery',
'required',
fn ($data, $index) => Arr::get($data, 'items.'.$index.'.asking_price') > 500 &&
Arr::get($data, 'items.'.$index.'.quantity') > 10
);
}

Accessor that decrypts model value isn't working

I have a trait that uses accessors and mutators to encrypt model values:
trait Encryptable
{
public function getAttribute($key)
{
$value = parent::getAttribute($key);
if (in_array($key, $this->encryptable)) {
$value = Crypt::decrypt($value);
return $value;
} else {
return $value;
}
}
public function setAttribute($key, $value)
{
if (in_array($key, $this->encryptable)) {
$value = Crypt::encrypt($value);
}
return parent::setAttribute($key, $value);
}
}
Comments Model
protected $fillable = ['content','user_id','commentable_id', 'commentable_type'];
protected $encryptable = [
'content'
];
CommentController
public function storePostComment(Request $request, Post $Post)
{
$this->validate($request, [
'content' => 'required',
]);
$comment = $post->comments()->create([
'user_id' => auth()->user()->id,
'content' => $request->content
]);
dd($comment->content);
//return new CommentResource($comment);
}
What's happening is that when I pass the return new CommentResource($comment); gives me the comments content encrypted, but dd($comment->content); decrypts the comments content. How do I decrypt the entire comment object so I can output it in a resource?
Edit For CommentResource
class CommentResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'content' => $this->content,
'owner' => $this->owner,
];
}
}
Edit 2 for answer
Here's my attempt:
use App\Comment;
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class CommentResource extends JsonResource
{
public function __construct(Comment $resource)
{
$this->resource = $resource;
}
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'content' => $this->content,
'owner' => $this->owner,
];
}
}
Error:
Argument 1 passed to App\Http\Resources\CommentResource::__construct() must be an instance of App\Http\Resources\Comment, instance of App\Comment given, called in /Applications/MAMP/htdocs/my-app/app/Http/Controllers/Api/CommentController.php on line 31
Edit 3 (final edit)
Here's what I figured out:
I tried a bunch of various combinations along with #Edwin Krause answer. I have another model using this encryptable trait and outputting in a resource that works fine.
To give a bit more context to this question I found out there was a problem using assertJsonFragment in a test:
CommentsTest
/* #test **/
public function a_user_can_comment_on_a_post()
{
$decryptedComment = ['content'=>'A new content']
$response = $this->json('POST', '/api/comment/' . $post->id, $decryptedComment);
$response->assertStatus(201);
$response->assertJsonStructure([
'data' => [
'owner',
'content'
]
])
->assertJsonFragment(['content' => $decryptedContent['content']]);
}
assertJsonFragment was returning the encrypted content and therefore failing because it was being tested against the decrypted comments content.
I used dd(new CommentResource($comment)); in the controller to check to see if it the content was decrypting, it wasn't.
I tried various different things trouble shooting with dd() in the controller method and even testing in the browser. Still nothing. I added #Edwin Krause code and still nothing on dd()
I finally got lucky and got rid of dd() with #Edwin Krause and changing my controller to:
Working code combined with #Edwin Krause answer in my CommentResource
$comment = Comment::create([
'user_id' => auth()->user()->id,
'content' => $request->content,
'commentable_type' => 'App\Post',
'commentable_id' => $post->id,
]);
return new CommentResource($comment);
The tests went green. I tried dd(new CommentResource($comment)); and the content was encrypted still. The content output on the broweser and assertJsonFragment worked. I must've tried so many combinations to try and figure this out and I kind of just got lucky.
I'm unsure as to why this is the way it is, but I've already spent hours on this, so I can't troubleshoot why it's breaking. Maybe someone else can.
Just a suggestion to try and override the constructor of the JsonResource and typecast the $resource parameter to your Modelclass.
It work's for other things, not sure if it fixes your issue, that needs to be tested
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Comment;
class CommentResource extends JsonResource
{
public function __construct(Comment $resource)
{
$this->resource = $resource;
$this->resource->content = $resource->content;
}
....
Edit:
I Played around a bit more with the constructor and the modified version should actually work. I don't have any encrypted data to play with, but logically this should work.

API display user details

My current API is:
[{"id":43,"title_id":1,"user_id":1,"comment":"asdasddsa","season":null,"episode":null,"created_at":null,"updated_at":"2019-04-30 09:14:15"}]
I want to replace user_id to the user details from the user table according to id. I would like to see it this:
[{"id":43,"title_id":1,"users":{id:"1",username:"test",email:"example#example.com"},"comment":"asdasddsa","season":null,"episode":null,"created_at":null,"updated_at":"2019-04-30 09:14:15"}]
Controller
public function index($id)
{
$test = Title::where('title_id', $id)->get();
return $test;
}
Title model
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
protected $table = 'title';
if I use foreach() in my controller like this:
$test = Title::where('title_id', $id)->get();
foreach($test as $details)
{
return $details->user->username;
}
It gets the user details, but it isn't in the API. How can I get it in API?
For API , it is better to Eloquent resource
Example in your Title resource it will be like
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Title extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'title_id' => $this->title_id,
'title_name' => $this->title_name,
'user_name' => $this->user->name,
];
}
public function with($request)
{
return [
'status' => '200',
'response' => 'Title resource',
];
}
}
In your controller
public function show(Title $title)
{
return new TitleResource($title);
}
In 5.4 it will be like
public function show(Title $title)
{
$title->user_name = $title->user->name;
return $title->toJson();
}
and it should work fine
example in tinker
>>> $user = App\User::find(1);
=> App\User {#3161
id: 1,
name: "superadmin",
email: "superadmin#example.com",
created_at: "2018-12-13 12:09:07",
updated_at: "2018-12-22 06:47:13",
}
>>> $user->pk = 's';
=> "s"
>>> $user
=> App\User {#3161
id: 1,
name: "superadmin",
email: "superadmin#example.com",
created_at: "2018-12-13 12:09:07",
updated_at: "2018-12-22 06:47:13",
pk: "s",
}
You can do this:
$test= Title::where('title_id', $id)->get();
And if I understand, there is one user for one title, correct? Then you can do this to retrieve the user of a title:
$test->user = User::find($test->user_id);
And finally return the title in a json response:
return response()->json($test, 200);
Your back will send what you want, something like this:
[{"id":43,"title_id":1,"users":{id:"1",username:"test",email:"example#example.com"},"comment":"asdasddsa","season":null,"episode":null,"created_at":null,"updated_at":"2019-04-30 09:14:15"}]

Dingo API remove "data" envelope

is it there an easy way to remove the "data" envelope from the Dingo API response.
When I use this Transformer to transform user models:
class UserTransformer extends EloquentModelTransformer
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'roles'
];
protected $defaultIncludes = [
'roles'
];
public function transform($model)
{
if(! $model instanceof User)
throw new InvalidArgumentException($model);
return [
'id' => $model->id,
'name' => $model->name,
'email' => $model->email
];
}
/**
* Include Roles
*
* #param User $user
* #return \League\Fractal\Resource\Item
*/
public function includeRoles(User $user)
{
$roles = $user->roles;
return $this->collection($roles, new RoleTransformer());
}
I get this response:
{
data : [
"id": 102,
"name": "Simo",
"email": "mail#outlook.com",
"roles": {
"data": [
{
"id": 1
"name": "admin"
}
]
}
}
]
}
I read some articles about RESTful APIs and a lot of them stated that such enveloped responses arent very modern (You should use the HTTP Header instead).
How can I disable this behaviour at least for the includes?
Thank you
For those who fall on this later and as I had really hard time to make it, I'd like to share how I made it working in my API :
1) Create a Custom Serializer, NoDataArraySerializer.php :
namespace App\Api\V1\Serializers;
use League\Fractal\Serializer\ArraySerializer;
class NoDataArraySerializer extends ArraySerializer
{
/**
* Serialize a collection.
*/
public function collection($resourceKey, array $data)
{
return ($resourceKey) ? [ $resourceKey => $data ] : $data;
}
/**
* Serialize an item.
*/
public function item($resourceKey, array $data)
{
return ($resourceKey) ? [ $resourceKey => $data ] : $data;
}
}
2) Set new the Serializer. In bootstrap/app.php, add :
$app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
$fractal = new League\Fractal\Manager;
$fractal->setSerializer(new App\Api\V1\Serializers\NoDataArraySerializer);
return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
});
That's it.
Now, in your UserController (for instance), you can use it like this :
namespace App\Api\V1\Controllers;
use App\Api\V1\Models\User;
use App\Api\V1\Transformers\UserTransformer;
class UserController extends Controller
{
public function index()
{
$items = User::all();
return $this->response->collection($items, new UserTransformer());
}
}
And the response will look like :
[
{
"user_id": 1,
...
},
{
"user_id": 2,
...
}
]
Or, I you want to add an enveloppe, you just need to set the resource key in the Controller. Replace :
return $this->response->collection($items, new UserTransformer());
by
return $this->response->collection($items, new UserTransformer(), ['key' => 'users']);
And the response will look like :
{
"users": [
{
"user_id": 1,
...
},
{
"user_id": 2,
...
}
]
}
One addition to the solution of YouHieng. The preferred way to register the NoDataArraySerializer in Laravel 5.3 and above is to write a custom ServiceProvider and add the logic into the boot() method and not the bootstrap/app.php file.
For Example:
php artisan make:provider DingoSerializerProvider
Then:
public function boot(){
$this->app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
$fractal = new League\Fractal\Manager;
$fractal->setSerializer(new App\Http\Serializers\NoDataArraySerializer());
return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
});
}
Have a look to http://fractal.thephpleague.com/serializers/#arrayserializer. They explain exactly what to do when
Sometimes people want to remove that 'data' namespace for items

Yii2 active record model not saving data

I've built a simple form model & view, a simple AR model, and a simple controller. The form model assigns the correct values to the AR instance, but when I call save(), none of those values are saved in the DB. Any ideas?
The form model:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
class PromptForm extends Model
{
public $name;
public $intro;
public $prompt;
public $notes;
public $questions;
public function attributeLabels()
{
return [
'name' => 'Prompt title',
'intro' => 'Intro',
'prompt' => 'Prompt body',
'notes' => 'Closing notes',
'questions' => 'Exploration questions',
];
}
/**
* #return array the validation rules.
*/
public function rules()
{
return [
[['name', 'prompt'], 'required'],
['name', 'filter', 'filter' => 'trim'],
['name', 'string', 'max' => 255],
[['intro', 'prompt', 'notes', 'questions'], 'default'],
];
}
public function post()
{
if ($this->validate()) {
$prompt = new Prompt();
$prompt->name = $this->name;
$prompt->intro = $this->intro;
$prompt->prompt = $this->prompt;
$prompt->notes = $this->notes;
$prompt->questions = $this->questions;
$prompt->author = \Yii::$app->user->getId();
//die(print_r($prompt, TRUE));
$prompt->save();
return $prompt;
}
return null;
}
}
The AR model:
<?php
namespace app\models;
use Yii;
use yii\db\ActiveRecord;
/**
* Prompt is the model behind the prompt item.
*/
class Prompt extends ActiveRecord
{
public $name;
public $intro;
public $prompt;
public $notes;
public $questions;
public $status;
public $author;
public $id;
/**
* #return string the name of the table associated with this ActiveRecord class.
*/
public static function tableName()
{
return 'prompt';
}
/**
* #return array the attribute labels.
*/
public function attributeLabels()
{
return [
'name' => 'Prompt title',
'intro' => 'Intro',
'prompt' => 'Prompt body',
'notes' => 'Closing notes',
'questions' => 'Exploration questions',
'status' => 'Status',
'author' => 'Author ID',
];
}
}
The controller:
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\filters\VerbFilter;
use app\models\PromptForm;
use app\models\Prompt;
class PromptsController extends Controller
{
public function actionIndex()
{
// Return a list of all prompts:
return $this->render('index');
}
public function actionNew()
{
if (\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new PromptForm();
if ($model->load(Yii::$app->request->post())) {
if ($prompt = $model->post()) {
Yii::$app->getSession()->setFlash('success', 'Your prompt was created successfully!');
return $this->goHome();
} else {
Yii::$app->getSession()->setFlash('error', 'Error while submitting your prompt.');
}
}
return $this->render('create', [
'model' => $model,
]);
}
}
Okay, I figured it out. Turns out that if you declare public attributes in your ActiveRecord model, they obscure the automatic attributes that are created by AR. Data gets assigned to your obscuring attributes but doesn't get sent into the database.
The correct AR model should have been simply this:
<?php
namespace app\models;
use Yii;
use yii\db\ActiveRecord;
class Prompt extends ActiveRecord
{
/**
* #return string the name of the table associated with this ActiveRecord class.
*/
public static function tableName()
{
return 'prompt';
}
}
Use
$prompt->save(false);
If that works that means that some validation rule fails.
Try
if ($model->load(Yii::$app->request->post())) {
if ($prompt = $model->post()) {
$model->save()
Yii::$app->getSession()->setFlash('success', 'Your prompt was created successfully!');
return $this->goHome();
} else {
Yii::$app->getSession()->setFlash('error', 'Error while submitting your prompt.');
}
}
In controller, change your if condition as follow :
if ($prompt = $model->post() !== null) {
This will validate that the value which is return is not null.
Your current validation condition is only validating where value is get assigned to variable $prompt or not. And that's why it's always returns true.
I ran across the same problem recently, when I combine the Active Record class with The Model class. Cause I know that AR actually extends Model in Yii2. Why not write less code.So I move the code from the Model to the AR.
$model = new User();
$model->load(Yii::$app->request->post())
But the AR's _attribute didn't get the post data in the form. the form data is actually in a Model object.
object(app\models\User)#39 (12) { ["password"]=> string(6) "google"
["newpass"]=> NULL ["name"]=> string(5) "Jane1" ["email"]=> string(16)
"jane#outlook.com" ["_attributes":"yii\db\BaseActiveRecord":private]=>
array(2) { ["password_hash"]=> string(60)
"$2y$13$.vNKpmosLjW/oYAhIezOZOj8rIG6QJvQj8tGHN2x78.75poXVn6Yi"
["auth_key"]=> string(32) "4XggNakVd-oeU28ny7obdw7gOmZJ-Rbu" }
simply delete the public attribute you want mass assign to the AR instance will make it work.
For who is struggling with this problem, I would remember to check the beforeSave method, if present. I mistakenly commented out the return statement.
public function beforeSave($insert)
{
// never toggle comment on this!!!
return parent::beforeSave( $insert);
}
How to Troubleshoot
First thing you should add while developing to your _form.php is errorSummary():
<?php $form = ActiveForm::begin(); ?>
// Some input fields
...
<?= $form->errorSummary($model); ?> // <--- Add this
...
<?php ActiveForm::end(); ?>
Simplify
Why not use scenarios instead if there is some minimal variation form to form:
In your model:
public function rules()
{
return [
[['field_1'], 'required', 'on' => self::SCENARIO_ADD], // only on add
[['field_2'], 'required', 'on' => self::SCENARIO_UPDATE], // only on update
[['field_3', 'field_4'], 'required'], // required all the time
];
}
In your controller:
public function actionAdd()
{
$model = new Model();
$model->scenario = Model::SCENARIO_ADD;
if ($model->load(Yii::$app->request->post())) {
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('add', ['model' => $model]);
}
Behaviors
Alternatively, rather than assign the user directly in your model, you could use a behavior like so:
https://www.yiiframework.com/doc/api/2.0/yii-behaviors-blameablebehavior
/**
* {#inheritdoc}
*/
public function behaviors()
{
return [
[
'class' => \yii\behaviors\BlameableBehavior::className(),
'value' => Yii::$app->user->identity->username,
],
[
'class' => \yii\behaviors\TimestampBehavior::className(),
'value' => new \yii\db\Expression('NOW()'),
],
[
'class' => 'sammaye\audittrail\LoggableBehavior',
'userAttribute' => 'updated_by', //blameable attribute of the current model.
'ignored' => ['updated_by', 'updated_at'], // This ignores fields from a selection of all fields, not needed with allowed
],
];
}

Resources