What is different between save(), create() function in laravel 5 - laravel

I need to know what is the difference of save() and create() function in laravel 5.
Where we can use save() and create() ?

Model::create is a simple wrapper around $model = new MyModel(); $model->save()
See the implementation
/**
* Save a new model and return the instance.
*
* #param array $attributes
* #return static
*/
public static function create(array $attributes = [])
{
$model = new static($attributes);
$model->save();
return $model;
}
save()
save() method is used both for saving new model, and updating
existing one. here you are creating new model or find existing one,
setting its properties one by one and finally saves in database.
save() accepts a full Eloquent model instance
$comment = new App\Comment(['message' => 'A new comment.']);
$post = App\Post::find(1);
$post->comments()->save($comment);
create()
while in creating method you are passing an array, setting properties in
model and persists in the database in one shot.
create() accepts a plain
PHP array
$post = App\Post::find(1);
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);
EDIT
As #PawelMysior pointed out, before using the create method, be sure to mark columns whose values are safe to set via mass-assignment (such as name, birth_date, and so on.), we need to update our Eloquent models by providing a new property called $fillable. This is simply an array containing the names of the attributes that are safe to set via mass assignment:
example:-
class Country extends Model {
protected $fillable = [
'name',
'area',
'language',
];
}

Related

Laravel Nova Actions BelongsTo field not working

I have this simple action:
/**
* Perform the action on the given models.
*
* #param \Laravel\Nova\Fields\ActionFields $fields
* #param \Illuminate\Support\Collection $models
* #return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
$model->update([
'user_id' => $fields->user
]);
}
}
/**
* Get the fields available on the action.
*
* #return array
*/
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
At first, it seems fine, but when I select User from BelongsTo relation and try to save exception is throwing:
Argument 1 passed to Laravel\Nova\Fields\BelongsTo::getRelationForeignKeyName() must be an instance of Illuminate\Database\Eloquent\Relations\Relation, instance of Illuminate\Support\Fluent given, called in /Users/rd/Sites/bns-crm/vendor/laravel/nova/src/Fields/BelongsTo.php on line 212
Yes i know i'm late but - here's a solution for this:
Use a Select-Field instead of BelongsTo and Pluck your options to build Key-Value pairs:
public function fields()
{
return [
Select::make('debitor')->options(\App\Models\Debitor::pluck('Name', 'id'))
];
}
Then in the handle you should geht the ids in $fields:
public function handle(ActionFields $fields, Collection $models) {
Log::info($fields);
}
Maybe I'm late, but, for the ones like me wanting to use the BelongsTo searchable field because the model they want to search in contains too much records to pack them in a normal Select field here is the solution I found:
Create a class in App\Nova\Fields with this code:
<?php
namespace App\Nova\Fields;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Http\Requests\NovaRequest;
class BelongsToForActions extends BelongsTo
{
public function fillForAction(NovaRequest $request, $model)
{
$attribute = $this->attribute;
if ($request->exists($attribute)) {
$value = $request[ $attribute ];
$model->{$attribute} = $this->isNullValue($value) ? null : $value;
}
}
}
Then use it like you would use a normal BelongsTo field. Just remember to fill the 3 arguments on the make, so, for example:
BelongsToForActions::make('User', 'relation', \App\Nova\User::class)->searchable()
Remember that 'relation' must exist.
Check your namespaces. Did you imported right class? User class must be resource class
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
I actually fixed this by mocking the key value pair used in this relationship.
First I build an array with the ID column as key and the name column as value.
$clients = Client::all()
->keyBy('id')
->map(fn($client): string => $client['name'])
->toArray();
Then I use the Select nova field to display it.
Select::make('Klant', 'client')
->searchable()
->options($clients)
->rules('required'),

Laravel - formatting all dates simultaneously

I'm currently working on a POC to showcase that it's going to be fairly painless to create an API with Laravel, the catch being that the database is already set in stone.
One problem I've run into is that they've used custom created at and updated at column names, e.g. for a car table, the created_at column would be car_time and the updated date would be cardata_time, and these are all saved as unix timestamps.
I know you can set the CREATED_AT and UPDATED_AT columns for each model. I want to go another step and return all dates in ISO 8601 format.
I've inserted a class between my models and Model called MasterModel and I want to do something like
protected function getCreatedAtAttribute($value)
{
$format = "Y-m-d\TH:i:s\Z";
$datetime = new DateTime($value);
return $datetime->format($format);
}
to make all created at dates be in that format. The problem is that I the custom created at and updated columns mean that this never gets called.
Is there a way for me to identify the created at and updated at columns in such a way that I can use a single method to updated all created at dates at the same time?
UPDATE: I realize my original question was not clear enough - I need to identify all fields that are dates, not just created_at and updated_at, and have them formatted a certain way. They will always be unix timestamps. Not sure how I'd go about this.
Here an answer that will expand on #caddy dz answer who happen to be sitting with me.
 All the things that need to be known
Deactivation of auto management of timestamps
public $timestamps = false; // <-- deactivate the automatic handling
Change table attributes names
const CREATED_AT = 'creation_date'; // <--- change the names
const UPDATED_AT = 'last_update';
source doc:
https://laravel.com/docs/5.8/eloquent#eloquent-model-conventions
By default, Eloquent expects created_at and updated_at columns to
exist on your tables. If you do not wish to have these columns
automatically managed by Eloquent, set the $timestamps property on
your model to false:
 Creating the accessors
class User extends Model
{
/**
* Get the user's first name.
*
* #param string $value
* #return string
*/
public function getFirstNameAttribute($value)
{
// do whatever you want here (change and mutate the value)
return ucfirst($value);
}
}
First thing to know, is that the accessors are a global concept for
eloquent and can be writing for all attributes and not just
getCreatedAtAttribute or getUpdatedAtAttribute.
Second thing to know is that whatever the name of the column, that is
in camle case (firstName) or with _ (first_name) eloquent know to
match to it. The format of the accessor should be
get[NameOfATtribute]Attribute in pascal case (camle case but first
letter too in uppercase).
Three the method argument hold the value of the column in
question. Bellow a snippet that show how it's used
$user = App\User::find(1);
$firstName = $user->first_name; //|=> first_name => getFirstNameAttribute(columnVal)
The resolution is clear.
first_name (column name) => getFirstNameAttribute(columnValue)
All the snippets are from the doc: https://laravel.com/docs/5.8/eloquent-mutators#accessors-and-mutators
 Let's apply all of that
First we need to not use $table->timestamps() in the migration so we make the changment to the bellow.
Schema::create('cars', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamp('cardata_time', 0)->nullable();
$table->timestamp('car_time', 0)->nullable();
});
Then we apply the modification on our model:
- we deactivate the auto handling of timestamps.
- Override the timestamps columns names.
- And create the accessors.
Here depend on what we want. If we want to only do the above here a snippet that show that:
// deactivate auto timestamps management
public $timestamps = false;
// change the columns names
const CREATED_AT = 'car_time';
const UPDATED_AT = 'cardata_time';
// creating the accessors (respect the naming)
protected function getCarTimeAttribute($value) //car_time => CarTime
{
// <-- do whatever you want here (example bellow)
$format = "Y-m-d\TH:i:s\Z";
$datetime = new DateTime($value);
return $datetime->format($format);
}
protected function getCardataTimeAttribute($value) //cardata_time => CardataTime
{
// <-- do whatever you want here
$format = "Y-m-d\TH:i:s\Z";
$datetime = new DateTime($value);
return $datetime->format($format);
}
 Doing it with renaming the attributes completely
If what you want is to use another accessing name. Then what my friend #caddy dz did is the way to go. Which happen to be sitting with me. And dared me to expand upon the answer. (hhhh)
You will need to know
$appends and $hidden
Part of the serialization API.
https://laravel.com/docs/master/eloquent-serialization#appending-values-to-json
https://laravel.com/docs/master/eloquent-serialization#hiding-attributes-from-json
$appends allow us to add attributes to the model. That don't exists on the table. We need also to create an accessors for them.
class User extends Model
{
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['is_admin'];
// ........
/**
* Get the administrator flag for the user.
*
* #return bool
*/
public function getIsAdminAttribute()
{
return $this->attributes['admin'] == 'yes';
}
}
and
$hidden allow us to remove and limit the attribute from the models. Like with the password field.
Doc examples:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = ['password'];
}
And from that what we need to do is to hide the attributes holding the time, that want to be changed to somehting else.
// remove the old attributes names
protected $hidden = ['car_time', 'cardata_time']; // renaming those
// append the new one \/ \/ <- to those
protected $appends = ['car_crated_at', 'cardata_created_at']; // names just for illustration
protected function getCarCreatedAtAttribute($value) // car_created_at => CarCreatedAt
{
$format = "Y-m-d\TH:i:s\Z";
$datetime = new DateTime($value);
return $datetime->format($format);
}
protected function getCardataCreatedAtAttribute($value) // cardata_created_at => CardataCreatedAt
{
$format = "Y-m-d\TH:i:s\Z";
$datetime = new DateTime($value);
return $datetime->format($format);
}
 Applying it for different models
The basic idea is to create a base model then extend it when you create your model.
Formatting all time attributes of the model without exception
If what you want is to apply the formatting for all the time attributes within the model.
Then override serializeDate() method. In practice write a trait, and then you can apply it. Otherwise a base model.
The answer bellow cover it well:
https://stackoverflow.com/a/41569026/7668448
And historically This thread is interesting :
https://github.com/laravel/framework/issues/21703
Serializing in Carbon level
In the documentation laravel 5.7 and up (what i checked [doc only]) :
https://laravel.com/docs/master/eloquent-serialization#date-serialization
We can change the formatting at the level of carbon serialization. But it happen that there was a bug in the past. Normally fixed but i didn't try it. Bug was in 5.7 and fixed in 5.7 if i'm not wrong. The git link above discuss it.
Snippet:
class AppServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* #return void
*/
public function boot()
{
Carbon::serializeUsing(function ($carbon) {
return $carbon->format('U');
});
}
___THE_END ^ ^
Not sure what you're asking but if you have cardata_time and car_time in your table defined like this
Schema::create('cars', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamp('cardata_time', 0)->nullable();
$table->timestamp('car_time', 0)->nullable();
});
And a MasterModel like so
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['created_at', 'updated_at'];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = ['car_time', 'cardata_time'];
protected function getCreatedAtAttribute($value)
{
$format = "Y-m-d\TH:i:s\Z";
$datetime = new DateTime($value);
return $datetime->format($format);
}
protected function getUpdatedAtAttribute($value)
{
$format = "Y-m-d\TH:i:s\Z";
$datetime = new DateTime($value);
return $datetime->format($format);
}
Results:
{
"id": 1,
"created_at": "2019-09-02T20:31:38Z",
"updated_at": "2019-09-02T20:31:38Z"
}
As in the documentation. The first approach. This requires the dates, to be defined in the $dates property. This will only be triggered if the Model is serialized.
public class YourModel extends Model
{
protected $dateFormat = "Y-m-d\TH:i:s\Z";
}
You can also define it in a provider in the boot method. Which will trigger when a Carbon date is serialized.
public function boot()
{
Carbon::serializeUsing(function ($carbon) {
return $carbon->format("Y-m-d\TH:i:s\Z");
});
}

Eloquent save() throws internal server error

I'm building some API app using Slim3 and Eloquent as ORM for model, and having some strange issue.
When I save model, I get internal server error, and can't catch error at all.
My model have only two columns, id pk autoincrement and name (string 255)
this is my settup:
Model:
class Version extends Illuminate\Database\Eloquent\Model
{
protected $table = 'version';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name'];
protected $hidden = ['id'];
}
Action:
public function create(RequestInterface $request, ResponseInterface $response)
{
$params = $request->getParsedBody();
$class = $this->model; //fully namespaced class name
$model = new $class();
$model->fill($params);
return $model->save() ?
$this->success($response, "{$this->getModelName()} successfully saved.") :
$this->error($response, $model->errors());
}
I manager to localize issue to this part of code in Illuminate\Database\Eloquent\Builderline #1242
if (in_array($method, $this->passthru)) {
return call_user_func_array([$this->toBase(), $method], $parameters);
}
But I have no idea what is wrong
EDIT:
I'm using PHP/7.0.0-dev and Slim 3.1
Is Eloquent PHP 7 compatible?
Your Model is a Eloquent Model so to create you can use following code
$version = App\Version::create(['name' => 'Your version name']);
This will remove all the unnecessary functions, and keept yuor code clean.
In your case make sure the $request->getParsedBody();
returns the correct format for Eloquent to use it.

Trouble getting attribute of relation in Laravel

I'm having a trouble with a relation in Laravel 5. the thing is that I have a table User and that user belongs to a Group for that, in the User model I have this:
public function group(){
return $this->belongsTo('App\models\Group');
}
The model Group have this attributes: name,unity,level, init_date. I also put there a default function to return a group as String, this is the code:
public function __toString(){
return $this->name.' Unity '.$this->unity;
}
So, the thing that in a view a have many users and for each of them I want to display the unity, name,date. When I call $user->group it returns me correctly the name and the unity in a String (because the _toString function) that means that he is really querying the group perfectly, but then, when I want to access a simple attribute as unity,date,or name with $user->group->name Laravel gives me this error:
Trying to get property of non-object
I even tried $user->group()->name then I gets: Undefined property: Illuminate\Database\Eloquent\Relations\BelongsTo::$name
Edited:
The model User:
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
use Authenticatable, CanResetPassword;
protected $table = 'users';
protected $fillable = ['email', 'password','estate','filial_id','perfil_id','rol','cat_teacher'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
public function __toString(){
return $this->email;
}
public function filial(){
return $this->belongsTo('App\models\Filial');
}
public function perfil(){
return $this->belongsTo('App\models\Perfil','perfil_id');
}
public function grupo(){
return $this->belongsTo('App\models\Group','group_id','id');
}
}
The model Group:
class Group extends Model {
protected $table = 'groups';
protected $fillable = ['name','unity','date'];
public function filiales() {
return $this->belongsTo('App\models\Filial');
}
public function teacher(){
return $this->belongsTo('App\models\User','teacher_id');
}
public function users() {
return $this->hasMany('App\models\User');
}
}
Then, in the controller I made a dd($users) and there not appear the relations, appears other relations but not this one. In the view I want to print some of the attributes in a table, for that I have:
<td>{{$user->group}}</td>
<td>{{$user->group->unity}}</td>
The first line works perfectly, and I don´t know why.
The reason you're unable to return your group's name is that ->group() returns an instance of the query builder, and not an eloquent collection/object. Since a user belongs to a single group, modify your code in one of these two ways:
public function group(){
return $this->belongsTo('App\models\Group')->first();
}
And then access the group using one of the following methods:
$user = User::with("group")->where("id", "=", 1)->first();
$group = $user->group;
echo $group->name;
// OR
$user = User::where("id", "=", 1)->first();
$group = $user->group();
echo $group->name;
Or, leave the group() function as it is and access ->group()->first() on your $user object:
$user = User::where("id", "=", 1)->first();
$group = $user->group()->first();
echo $group->name;
Any of the above methods should properly return your group object's name (or other attributes). Check the Eloquent Documentation for detailed explanations on how to access these objects.

Eloquent's default $attributes don't get mutated

It seems that default values defined in the $attributes property are not being mutated before saving to the database?
I have this setup:
protected $attributes = array(
'data' => array();
);
public function getDataAttribute($value)
{
return unserialize($value);
}
public function setDataAttribute($value)
{
$this->attributes['data'] = serialize($value);
}
All I get is database field filled with literally 'Array' and a preg_replace error. Upon specifying the default attribute in an overridden save() method it does get mutated.
Can anyone confirm this and/or has suggestions?
Thanks.
You are assigning a default variable on the object itself, which is outside the function of a mutator.
The best option is probably to include the default in the constructor of the model, so they are called and mutated when the object is created. I think something like this should work:
function __construct($attributes = array()) {
parent::__construct($attributes);
$this->data = array();
}

Resources