In Laravel 5.4 when I try to save User model to the database the values are not saved. I've set the fillable property as well.
It was working in Laravel 5.3. This issue is coming after upgrading the application into Laravel 5.4.
Below is a User model.
class User extends BaseModel implements AuthenticatableContract, CanResetPasswordContract, JWTSubject
{
use SoftDeletes,
UserAccess,
UserAttribute,
UserRelationship,
Authenticatable,
CanResetPassword,
Notifiable;
/**
* Database Table
*
* #var string
*/
protected $table = "users";
/**
* The attributes that are not mass assignable.
*
* #var array
*/
protected $guarded = ['id'];
/**
* Fillable Form Fields
*
* #var array
*/
protected $fillable = [
'name',
'first_name',
'last_name',
'email',
'password',
'status',
'confirmed',
'api_user',
'confirmation_code',
'account_id',
'role_id',
'cw_contact_id',
'all',
'all_locations',
'username',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* Select HTML Preference
*
* #var string
*/
protected static $selectHTMLFormat = "[email]";
/**
* #var array
*/
protected $dates = ['deleted_at', 'last_login'];
}
Please note the issue is with User Model only.
I'm saving User as below.
// Create User
$user = $this->model->create([
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'username' => $input['username'],
'email' => $input['email'],
'password' => bcrypt($input['password']),
'confirmation_code' => md5(uniqid(mt_rand(), true)),
'confirmed' => 1,
'api_user' => (isset($input['api_user']) ? $input['api_user'] : 0),
'account_id' => $input['account_id'],
'role_id' => (isset($input['role_id']) ? $input['role_id'] : 0),
'all' => (!isset($input['associated-permissions']) || $input['associated-permissions'] == 'all') ? 1 : 0,
'status' => (!isset($input['status']) || $input['status'] ? 1 : 0),
'all_locations' => $input['all_locations']
]);
Then the create method of BaseModel will be called and below is the code of it.
public static function create(array $attributes = Array())
{
$user = access()->user();
if($user)
{
$attributes['account_id'] = (!isset($attributes['account_id']) ? $user->account->id : $attributes['account_id'] );
}
$childClass = get_called_class();
$model = new $childClass;
$model->runActionLogger(false, 'create');
return parent::query()->create($attributes);
}
The reason is most probably the new middleware in Laravel 5.4 called "Request Sanitization Middleware" as explained in https://laravel.com/docs/5.4/releases.
Disable \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, in app/Http/kernel.php and see what you get.
You can also check in /config/database.php and your mysql connection settings: 'strict' => true, if so, set to false.
A good practice is using the model for user input. In this case, instead of $user = $this->model->create(...) populate you model with
$user = new \App\User($input) and update your values from there, f.ex.
$user->confirmation_code = md5(uniqid(mt_rand(), true)); and
$user->password = bcrypt($user->password);
If fields are nullable, indicate as such in your migration file, f.ex. $table->string('all')->nullable();
If done just run $user->save();
From 5.4 the create() function is not more defined in Illuminate\Database\Eloquent\Model:
Is handled as dinamic method call, that is by calling one of these functions (dependig on if it's called statically or not):
public static function __callStatic($method, $parameters)
// or
public function __call($method, $parameters)
In the Illuminate\Database\Eloquent\Model class.
Now I dont have all your code but, IMHO, I will try to change this line in your BaseModel class:
return parent::query()->create($attributes);
to this:
return $model->create($attributes);
or, even better for me, to this:
return (new static)->newQuery()->create($attributes);
In the
documentation
it says:
$post = App\Post::find(1);
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);
So
$user = $this->users()->create([
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'username' => $input['username'],
'email' => $input['email'],
'password' => bcrypt($input['password']),
'confirmation_code' => md5(uniqid(mt_rand(), true)),
'confirmed' => 1,
'api_user' => (isset($input['api_user']) ? $input['api_user'] : 0),
'account_id' => $input['account_id'],
'role_id' => (isset($input['role_id']) ? $input['role_id'] : 0),
'all' => (!isset($input['associated-permissions']) || $input['associated-permissions'] == 'all') ? 1 : 0,
'status' => (!isset($input['status']) || $input['status'] ? 1 : 0),
'all_locations' => $input['all_locations']
]);
where users() is your public function but I don't know what is $this in your case but should be the model as in the example from documentation.
Why you're not using resource controllers? Or if you need to populate the db use a seeder
I think It will be more easy to manage.
So 2 things i can think off
1st there is no need to use
protected $guarded = [];
and
protected $fillable = [];
Guarded will assume everything is fillable if its not in here and fillable will assume everything is guarded unless in here.
To quote the docs
While $fillable serves as a "white list" of attributes that should be mass assignable, you may also choose to use $guarded. The $guarded property should contain an array of attributes that you do not want to be mass assignable. All other attributes not in the array will be mass assignable. So, $guarded functions like a "black list". Of course, you should use either $fillable or $guarded - not both.
2nd to rule out any of the $this->model stuff try instantiate the class first and save them
use App\Path\To\Model as user;
$user = new user();
$user->first_name = $input['first_name'];
$user->last_name = $input['last_name'];
$user->username = $input['username'];
$user->email = $input['email'];
$user->password = bcrypt($input['password']);
$user->confirmation_code = md5(uniqid(mt_rand(); true));
$user->confirmed = 1;
$user->api_user = (isset($input['api_user']) ? $input['api_user'] : 0);
$user->account_id = $input['account_id'];
$user->role_id = (isset($input['role_id']) ? $input['role_id'] : 0);
$user->all = (!isset($input['associated-permissions']) || $input['associated-permissions'] == 'all') ? 1 : 0;
$user->status = (!isset($input['status']) || $input['status'] ? 1 : 0);
$user->all_locations = $input['all_locations'];
$user->save();
Guys I'm able to resolve issue by using Fill() method.
public static function create(array $attributes = Array())
{
$user = access()->user();
if($user)
{
$attributes['account_id'] = (!isset($attributes['account_id']) ? $user->account->id : $attributes['account_id'] );
}
$childClass = get_called_class();
$model = new $childClass;
$model->fill($attributes);
$model->save();
$model->runActionLogger($model, 'create');
return $model;
}
Also By mistake I've added Construction on CanResetPassword Trait which causing issue as well. So If i remove that everything will work as before as well.
Yes, you can't use __construct method in Traits.
Please Refer PHP.net for more details about trait, they said "Using AS on a __construct method (and maybe other magic methods) is really, really bad."
You can use trait like following way.
class User extends BaseModel
{
use userRelation
}
Trait userRelation
{
public function getUserName()
{
return "Jhon Doe";
}
}
I have created "userRelation" Trait which contains few useful code to re-use.
Please refer following link for more details - http://php.net/manual/en/language.oop5.traits.php
Please try it and let me know if it won't work.
Thanks
Related
Part of data can not be acquired when Eloquent object is set to Json.
I use command artisan make:Auth, and customized users table.
Specifically, I thought that I did not need to delete the email column once,but since it became necessary, I added it again.
These operations use a migration file.
I got all the data of users table and returned it with Json.
But email is not output.
class UserController extends Controller
{
public function index() {
return User::all();
}
}
Even using json_encode did the same thing.
It exists when use this
$user = User::all();
$userArray = array();
foreach ($user as $u){
$userArray[] = [
'name' => $u->name,
'email' => $u->email
];
}
return $userArray;
Why is not email being outputted?
(I rely on google translation)
Additional notes
Thanks to everyone for comment.
Comparing the output of json with dd(), password and remember_token were not output in addition to email.
Perhaps the email is private property, and private property is a mechanism that does not output JSON?
In that case, how can I change to public?
The User model looks like this
class User extends Authenticatable
{
use Notifiable;
use SoftDeletes;
protected $table = 'users';
protected $dates = ['deleted_at'];
protected $primaryKey = 'uuid';
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'email','remember_token',
];
~~~
}
I solved it myself.
I found that the column set here is not output to JSON.
protected $hidden = [
'password', 'email','remember_token',
];
It was a very simple thing....
try this:
return User::select('name', 'email')->get()->toArray();
im trying to run some additional code when a row is deleted using my Model. However the callback statis::deleted simply isn't being triggered.
Controller:
/**
* #param Website $website
* #param Request $request
* #return \Illuminate\Http\RedirectResponse
* #throws \Exception
*/
public function delete(Website $website, Request $request)
{
$id = $request->input('id-value');
WebsiteRedirects::query()->where(['website_id' => $website['id'], 'id' => $id])->delete();
Session::flash('message', [ 'is-success' => [ '1 Redirect has been deleted!' ] ]);
return back();
}
Model:
class WebsiteRedirects extends Model
{
protected $table = 'website_redirects';
protected $guarded = [];
public $timestamps = false;
protected static function boot()
{
parent::boot();
static::saved(function ($redirect) {
PlannerStatus::status('redirect', $redirect->website_id, 1);
});
static::deleted(function($redirect) {
dd('deleted');
PlannerStatus::status('redirect', $redirect->website_id, 1);
});
}
...
static::saved works fine, and I insert using query too.
WebsiteRedirects::query()->create(
[
'website_id' => $website->id,
'redirect_from' => $request->input('redirect-from'),
'redirect_to' => $request->input('redirect-to')
]
);
The event is not being called because you are not deleting the row via Eloquent. You are deleting the row directly, without fetching the result - therefor Eloquent can't run the deleted event.
You will have to fetch the model before deleting for the event to be triggered.
WebsiteRedirects::where(['website_id' => $website['id'], 'id' => $id])->first()->delete();
Add first() to retrieve the WebsiteRedirect before you run delete()
In your code
WebsiteRedirects::query()->where(['website_id' => $website['id'], 'id' => $id])
right before the delete() method, the instance of the object is Illuminate\Database\Eloquent\Builder not your model. wich will trigger the Eloquent delete (DB) not your model's one.
Normaly you would do something like:
$user = User::find($id);
$user->delete();
I am trying to do the mass assignment with laravel like. But I have a field called 'hidden' that in the database is a TINYINT. From my front-end I get a boolean back. When I mass-assign with 'hidden' => TRUE the field in DB still is 0. When i convert it back to a integer ('hidden' => 1) then the field is saved as 1.
I did added 'hidden' to my $fillable.
P.S. When I try inserting it into DB directy with mysql with boolean value, it works.
Anyone know what is wrong?
EDIT: this is my code,
public function store(Request $request) {
class Group extends Model
{
use Notifiable;
const CREATED_AT = 'created';
const UPDATED_AT = 'updated';
protected $table = 'groups';
protected $casts = [
'hidden' => 'boolean',
];
protected $fillable = [
'hidden',
// etc
];
}
public function store(Request $request) {
$post = $request->all();
$group_id = Group::create($post);
}
Front-end is Vue project. So laravel is my API. And I do get a TRUE out of $post['hidden'].
You need to cast the boolean in the model:
class YourModel extends Model
{
protected $casts = [
'hidden' => 'boolean',
];
}
This will tell Laravel the you want the hidden column to be treated as boolean and values like 0 and 1 will be returned as true/false and true/false saved as 0/1.
You can read more in Laravel doc mutators.
Change the database type to Bool. If you want to do that with a migration you can do: $table->boolean(‘hidden’);
I created the model:
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class ClientModel extends Eloquent implements UserInterface, RemindableInterface {
protected $connection = 'local_db';
protected $table = 'administrators';
protected $fillable = ['user_id'];
public function getAuthIdentifier()
{
return $this->username;
}
public function getAuthPassword()
{
return $this->password;
}
public function getRememberToken()
{
return $this->remember_token;
}
public function setRememberToken($value)
{
$this->remember_token = $value;
}
public function getRememberTokenName()
{
return 'remember_token';
}
public function getReminderEmail()
{
return $this->email;
}
}
When I try to use it like this:
ClientModel::create(array(
'username' => 'first_user',
'password' => Hash::make('123456'),
'email' => 'my#email.com'
));
It creates empty entry in DB...
I think you make it too complicated. There is no need to make it this way. By default you have User model created and you should be able simple to create user this way:
$user = new User();
$user->username = 'something';
$user->password = Hash::make('userpassword');
$user->email = 'useremail#something.com';
$user->save();
Maybe you wanted to achieve something more but I don't understand what you use so many methods here if you don't modify input or output here.
You are using create method (Mass Assignment) so it's not working because you have this:
// Only user_id is allowed to insert by create method
protected $fillable = ['user_id'];
Put this in your model instead of $fillable:
// Allow any field to be inserted
protected $guarded = [];
Also you may use the alternative:
protected $fillable = ['username', 'password', 'email'];
Read more about Mass Assignment on Laravel website. While this may solve the issue but be aware of it. You may use this approach instead:
$user = new User;
$user->username = 'jhondoe';
// Set other fields ...
$user->save();
Nowadays way :
User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
or even:
$arrLcl = [];
$arrLcl['name'] = $data['name'];
$arrLcl['email'] = $data['email'];
$arrLcl['password'] = $data['password'];
User::create($arrLcl);
I'm trying to send use models for the first time and running into a confusion. When I run a query, the rules are linked with it, is it supposed to be like that?
Model:
class User extends Elegant
{
public static $table = 'users';
protected $rules = array(
'email' => 'required|email',
'firstname' => 'required',
'lastname' => 'required',
'initials' => 'required|alpha|match:/[A-Z]+/',
'role' => 'required|in:writer_fr,writer_en,reader',
'password' => 'min:6,max:32|same:password2'
);
public static function has_role($role)
{
//$u = new User;
$users = User::where($role, '=', 1)->get(array('firstname', 'lastname'));
return $users;
}
}
Controller
$u = array();
$u['writer_en'] = User::has_role('writer_en');
dd($u['writer_en']);
Which prints out the entire model rules, messages, relationship etc logic. Am I doing something wrong or is this normal?
In your has_role method you are returning User model
public static function has_role($role)
{
//$u = new User;
$users = User::where($role, '=', 1)->get(array('firstname', 'lastname'));
return $users; // <-- User model
}
So, it's dumping the User model and it's doing the right thing as it suppose to do by following code
$u = array();
$u['writer_en'] = User::has_role('writer_en');
dd($u['writer_en']);
Instead of dumping the model, you can use
$user = User::has_role('writer_en');
echo $user->firstname;
echo $user->lastname;