Laravel fill column value with default value while saving - php

I want to fill the token field in users table with value while saving data to table.
How to achieve both in query builder and eloquent.

You can use the Model events adding a boot method into Model class like this.
public static function boot()
{
parent::boot();
static::created(function ($model) {
$model->token = str_random(40);
});
}
Also you can create an observer to do the same and then to add the protected $dispatchesEvents array into the model.

Related

Remove specific field from model before creating after processing laravel

I am trying to make a trait for storing images for models. I am not able to remove the thumbnail from the model.
Is there any way to remove the thumbnail field from the model because there is no field like a thumbnail in the table?
trait ModelHelpers
{
protected static $thumbnail;
public static function boot()
{
parent::boot();
self::creating(function($model){
$collection = collect($model);
self::$thumbnail = $collection->only('thumbnail');
$collection->except(['thumbnail']);
$model->ignoreField('thumbnail');
// ... code here
});
}
}
OR
Is there any way to add the data in the model that don't process while mysql query but is available in the model for processing before or after creating?
Right now I am adding the thumbnail key in fillable to get into the model but it is processed while the insert query that i don't want to:
protected $fillable = ['user_id', 'title', 'meta_title', 'slug', 'summary', 'published','published_time', 'thumbnail'];
$fillbale is used to define the properties you want to use in inserting.
If you want to skip some properties to insert it, you must use $guarded

how to require attaching related resources upon creation of resources - Laravel Nova

I have a model called Tree that is supposed to be associated to 1..n Things. Things can be associated to 0..n things. In other words this is a many-to-many relationship, and a Thing must be chosen when a Tree is being created. My thing_tree migration looks like this (there's also a thing_thing pivot table but that's irrelevant):
public function up()
{
Schema::create('thing_tree', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->unsignedBigInteger('tree_id')->nullable();
$table->unsignedBigInteger('thing_id')->nullable();
$table->unique(['tree_id', 'thing_id']);
$table->foreign('tree_id')->references('id')->on('trees')->onDelete('cascade');
$table->foreign('thing_id')->references('id')->on('things')->onDelete('cascade');
});
}
My Tree model looks like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tree extends Model
{
use HasFactory;
protected $guarded = [];
public function path(){
$path = '/trees/' . $this->id;
return $path;
}
public function associatedThings () {
return $this->belongsToMany(Thing::class);
}
}
The Thing model looks like this:
public function trees()
{
return $this->belongsToMany(Tree::class);
}
public function parentOf (){
return $this->belongsToMany(Thing::class, 'thing_thing', 'parent_id', 'child_id');
}
public function childOf(){
return $this->belongsToMany(Thing::class, 'thing_thing', 'child_id', 'parent_id');
}
Finally, the Tree Nova resource has these fields:
public function fields(Request $request)
{
return [
ID::make(__('ID'), 'id')->sortable(),
Text::make('name'),
ID::make('user_id')->hideWhenUpdating()->hideWhenCreating(),
Boolean::make('public'),
BelongsToMany::make('Things', 'associatedThings')
];
}
It should not be possible to create a Tree without an attached Thing, but the creation screen looks like this:
How do I require this in Nova?
This is not possible through nova's default features. Here is how I would go about it with the least effort (you Might want to create a custom field for that yourself) - or at least how I solved a similar issue in the past:
1. Add the nova checkboxes field to your project
2. Add the field to your nova ressource :
// create an array( id => name) of things
$options = Things::all()->groupBy('id')->map(fn($e) => $e->name)->toArray();
// ...
// add checkboxes to your $fields
Checkboxes::make('Things', 'things_checkboxes')->options($options)
3. Add a validator that requires the things_checkboxes to be not empty
4. Add an observer php artisan make:observer CheckboxObserver that will sync the model's relations with the given id-array through the checkboxes and then remove the checkboxes field from the object (as it will throw a column not found otherwise), so something like this:
public function saving($tree)
{
// Note: In my case I would use the checkbox_relations method of the HasCheckboxes trait and loop over all checkbox relations to perform the following and get the respective array keys and relation names
$available_ids = array_unique($tree['things_checkboxes']);
// Attach new ones, remove old ones (Relation name in my case comes from the HasCheckboxes Trait)
$tree->things->sync($available_ids);
// Unset Checkboxes as the Key doesn't exist as column in the Table
unset($tree['things_checkboxes']);
return true;
}
5. Add the same thing in reverse for the retreived method in your observer if you want to keep using the checkboxes to handle relations. Otherwise, add ->hideWhenUpdating() to your checkbox field
I added a trait for that to easily attach the relations through checkboxes to a model:
trait HasCheckboxRelations
{
/**
* Boot the trait
*
* #return void
*/
public static function bootHasCheckboxRelations()
{
static::observe(CheckboxObserver::class);
}
/**
* Defines which relations should be display as checkboxes instead of
* #return CheckboxRelation[]
*/
public static function checkbox_relations()
{
return [];
}
}
And checkbox_relations holds an array of instances of class CheckboxRelation which again holds informations about the key name, the relation name and so on.
public function __construct(string $relationName, string $relatedClass, string $fieldName, bool $hasOverrides = false, string $relationType = null, array $_fields = [])
Also, I added a method attachCheckboxRelationFields to the default nova resource which will be called on the $fields when the model uses the trait.
Now, I only have to add HasCheckboxRelations to a model, add the array of checkbox_relations and thats it - I have a belongsToMany relation on the nova resource through checkboxes. Of course you don't have the option to manage pivot fields anymore if you go for it this way - which might be why it was not done by the nova devs - but for simple belongsToMany relations I really like to work with the checkbox solution instead of the default attach-table. And for data with pivot fields you can still use the default way.
Also note that parts of the code where written on the fly so it might not work out of the box, but the overall idea should be delivered.
Hope it helped!
alternative
https://github.com/Benjacho/belongs-to-many-field-nova
BelongsToManyField::make('Role Label', 'roles', 'App\Nova\Role'),

Is there a way to manipulate inputs in Laravel Model before storing them?

In Laravel, I have used a model's create method in many controllers,
Now I need to perform strip_tags($comment) to a specific input in all those controllers before it is inserted in database with create() method like this:
Comment:create([
'comment' => $comment,
...
]);
Should I repeatedly do this in all controllers:
$comment = strip_tags($comment); // < Is it possible to do this on model's file so we don't repeat it every time?
Comment:create([
'comment' => $comment,
...
]);
Or this is something that can be achieved in the Model?
You may use model events to make checks and arrangements before saving it.
add following method to your model class;
protected static function boot()
{
parent::boot();
self::saving(function ($model) {
$model->comment = strip_tags($model->comment);
// do your pre-checks or operations.
});
}
here is a useful post to read about it
There is a way to do it directly in the model, it's called Mutators. If your column name is comment then the mutator function will be called setCommentAttribute.
public function setCommentAttribute($comment)
{
$this->attributes['comment'] = strip_tags($comment);
}
Any place where save/update is used for this model, the comment data will go through the set... function.

Get relationships after insert

In my code, I insert a new row into the database:
$post = new Post;
$post->user_id = Auth::user()->id;
// more inserts
$post->save();
In my Post.php, I have:
protected $with = [
'user', 'answers', 'questions'
];
public function users()
{
return $this->belongsTo('App\User');
}
// etc
But when I return $post after I insert, there are no relationships (users, answers, questions) attached to it.
How can I get all of the default relationships to load after an insert?
The save() method persists the data to the database, but it doesn't do anything about refreshing the data on the Model or reloading relationships.
The easiest solution would be to refresh your object after calling save(). This will automatically eager load the relationships you've defined in your $with property on the model:
// ...
$post->save();
// refresh the post from the database
$post = $post->fresh();
Another option is to just manually reload the relationships yourself, using the load() method.
// ...
$post->save();
// reload the desired relationships
$post->load(['user', 'answers', 'questions']);
However, this duplicates the code that defines the relationships you'd like to be auto loaded (defined once in the Model, and then once in this code). You can mitigate that by creating a new function on your Model.
// in Post model
public function reloadRelations() {
$this->load($this->with);
}
// code usage
// ...
$post->save();
// call your new function to reload the relations
$post->reloadRelations();
However, the only real benefit of going this route over just calling the built in fresh() method is that this won't re-run the query to get the original Post data.
If you're handling 1000s of requests a second, maybe the one query might make a difference, but other than that, I wouldn't worry about it, and just use the fresh() method. But, the options are here for you to choose.
Instead of manually setting the attribute user_id, you should use the associate method from \Illuminate\Database\Eloquent\Relations\BelongsTo class.
$post->user()->associate(Auth::user());
// now you have the user inside your post.
dd($post->user);
May be, in model Post.php:
protected $primaryKey = 'id';
public function users()
{
return $this->hasOne('App\User', 'id', 'user_id');
}
before:
migration "posts"
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
// .....
});
Hopefully this will solve your problem

How do I get the `pivot table` model to trigger the save/saved model events when attach or detach are called in Laravel?

In Laravel 4 how do I get the pivot table model to trigger the save/saved model events when attach or detach are called?
It seems that the pivot table 'TeamUser' below is not actually needed for the attach/detach methods to work, so at a guess I believe that the model representing the pivot table is never invoked. And so the events are never triggered.
To ask another way:
When I call User::with('Team')->find(1)->teams()->attach(1); how to get TeamUser to trigger it's own events. Note that the above attach works perfectly fine, all the records are updated in the database.
User
class User extends Eloquent
{
// Relationship
public function teams()
{
return $this->belongsToMany('Team');
}
}
Team
class Team extends Eloquent
{
// Relationship
public function users()
{
return $this->belongsToMany('User');
}
}
Team User - The model for the TeamUser table/pivot table
class TeamUser extends Eloquent
{
public function save(array $options = array())
{
// do cool stuff when relationship is saved via
// attach/detach
}
}
You can add a $touches to the Team model.
class Team extends Eloquent
{
protected $touches = array('TeamUser');
// Relationship
public function users()
{
return $this->belongsToMany('User');
}
}
This will update the TeamUser updated_at field. I think this will also hit the save() method in the TeamUser model.
If that doesn't work, you can fire your own event in the Team save() method and listen to this event somewhere.
Event::fire('team.updated', array($team));
With the new version of Laraval 5.8, Pivot tables trigger the boot events. Check the release notes: https://laravel.com/docs/5.8/releases#pivot

Categories