I am using laravel 8 with an existing user table. All is working as expected except the password reset link functionality. This is because my table has the email column name as "Email" instead of "email." Other applications use this table, so the column name cannot be changed. I can get the password reset link functionality working if I manually set the column name within the framework itself (example below).
File: /vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
public function retrieveByCredentials(array $credentials)
{
// framework code that retieves the user record for email address
if ($res) {
$res->email = $res->Email;
}
// rest of frame work code
}
This seems a little "hacky." Is there a better approach to this?
Laravel would benefit greatly from more customization regarding the user's table (custom user table name, column names, etc.).
Laravel has mutators and accessors. This does that you can change behavior of ->email access or assigning it. Add this snippet to your User.php model.
class User {
public function getEmailAttribute()
{
return $this->attributes['Email'];
}
}
You can read the docs about it. The naming convention for the function is get{PropertyName}Attribute, if you define your function like so, you can easily overwrite property logic in Laravel. Making it use the column Email.
Related
The users table, besides others, have these fields: username, first_name, last_name. Each user can decide whether to show the username or the full name (first + last). This choice is stored inside a "settings" table.
To not perform repeated queries and calls to a function any time I need to show the name, I add the name to display to the user object as it is created, like $user->display_name = ... according to the user's choice.
The problem is that when the user updates the profile, Laravel tries to save this name inside a display_name field into the users' table, which doesn't exist and returns a 500 error. That also happens when the user tries to logout.
Is it possible to avoid that Laravel tries to store that value inside the database?
As suggested in other discussions I have already tried to give a default value to the attribute inside the User model, I've tried to set the attribute as protected, but nothing did work.
This is where the get{...}Attribute() function of a Model comes in handy. Say you want to access $user->full_name without actually saving full_name to the database. Since you have first_name and last_name, you can declare on your User model:
public function getFullNameAttribute(){
return $this->first_name." ".$this->last_name;
}
Laravel will parse what's between get and Attribute into a property available on the model, in this case either $user->full_name or $user->fullName.
To translate this to your use case, you can use something like:
In your User.php model:
public function getDisplayNameAttribute(){
if($this->settings == "use_full_name"){
return $this->first_name." ".$this->last_name;
} else if($this->settings == "use_username"){
return $this->username;
}
return "Not Configured...";
}
Note: You'll have to configure your if statements to determine what to return based on your settings.
Somewhere in a controller or view, you can call $user->display_name to have one of 3 things (determined by the logic/return statements above) displayed:
public function example(){
$user = User::first();
dd($user->display_name);
// $user->first_name." "$user->last_name, $user->username or "Not Configured..."
}
By doing this, when you access your $user, it will have a display_name property available that doesn't actually exist on the model, so you won't run into issues should you call $user->save();
I added a couple of virtual columns to my database tables using Laravels virtualAs column modifier:
$table->decimal('grand_total')->virtualAs( '(total_value + (total_value*tax_rate))');
Basically it keeps a mysql virtual column that automatically calculates the grand total based on the total and tax rate stored in another column.
However, Laravel does not seem to play nice with virtual columns at all. When saving a record, it attempts to INSERT or UPDATE the virtual column, which is obviously not allowed in mySQL. I could not find a way to configure in the Eloquent model which fields are actually written to the database on an update or insert.
I've tried adding the field to the models $hidden, and $appends but nothing seems to work.
Looking at the Laravel Source code for an insert (https://github.com/laravel/framework/blob/5.6/src/Illuminate/Database/Eloquent/Model.php#L733), it seems to just insert whatever attributes are in $this->attributes. When the record is read from the database the grand_total field is read from the table and set as an attribute and then it is tried to be written again once the record is saved.
Is there any way to get this Laravel to stop trying to save columns that are virtual?
Here's a quick trait I wrote to solve your problem that will filter fields residing in the $virtualFields property before saving. It requires a select (refresh) after the save to get the new value for the virtual field. If you don't need to query this virtual field, I'd highly recommend you look into a mutator instead.
trait HasVirtualFields
{
public function save(array $options = [])
{
if (isset($this->virtualFields)) {
$this->attributes = array_diff_key($this->attributes, array_flip($this->virtualFields));
}
$return = parent::save($options);
$this->refresh(); // Refresh the model for the new virtual column values
return $return;
}
}
class YourModel
{
use HasVirtualFields;
protected $virtualFields = ['grand_total'];
}
I have a Resource Controller (with all the actions: index, create, store, show, edit, update and destroy) and I was wondering what is the best approach to edit a single field column?
Let's say we have a Users table with name, email, password and active (active is a tiny int 0 or 1).
In the users management page, there is a button to activate/deactivate users (makes a request to the server to update the "active" field for the selected user).
Should I create a new method updateStatus in the Controller or is there a way to handle this using the update method?
I don't want, by mistake, allow empty values in the name, email or password when updating the "active" column, so I need to keep the validation rules (in short, all fields are required), but this means when updating the "active" field, I need to pass all the user data in the request.
At this point I'm very confused and all help will be appreciated.
Thanks in advance!
When you send an instance from edit action to the form , all the data will be sent and you can edit one or more columns if you need .
For instance :
public function update(Request $request , $id) {
$data = YourModel::find($id);
$data->someColumn = $request->someColumn;
$data->save();
}
other fields that you didn't send any value for them will be saved as they were before . for this you can set the form like below :
{!! Form::model($yourInstance,['route'=>['someRoute.update','id'=>$yourInstance->id],'method'=>'PATCH',]) !!}
It sounds like you are new to Laravel, and some key concepts can be hard to grasp.
In my opinion the best way to do it would be via a Model class. This is slightly confused by the fact that Laravel has a built in Users model, so I'm going to use a different model as the example of how to update a db field.
php artisan make:model MyData
Will create a new empty model file for the MyData table in app/
The file will look like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MyData extends Model
{
//
}
Even though there's nothing in there, it now allows you do alter the database table using Eloquent.
In your controller add this to make sure the model is included:
use App\MyData as MyData;
The controller should have a method something like this if updating with user input from a form:
public function updateStatus(MyData $myData, Request $request){
$myData->where('id', $request->id)->update(['active' => $request->active]);
}
You could do the exact same thing like this:
public function updateStatus(Request $request){
$data = MyData::find($request->id);
$data->active = $request->active;
$data->save();
}
Both approaches make sense in different circumstances.
See https://laravel.com/docs/5.5/eloquent#updates
I'm working in a Webapp and I have a problems to work with a intermediate table, these are my tables in mysql:
User:
Integer:id
String:name
String:email
String:phone
Exercise:
Integer:id
String:name
String:description
User_Exercise:
Integer:id
Integer:id_user
Integer:id_exercise
Integer:record
So, what I want to do is that when I create an exercise, it be created one row for each user with the exercise-id that I have created it before. Later the user could change his record in this exercise.
I have thought to create a model to handle the user_exercise's table but I don't know if there is some way to do this better or not.
So, There are some way to do this without create a new model?
PD: Sorry for my terrible english
You don't need a seperate model for User_Exercise
You can use $this->belongsToMany from base Model i.e., User
Note :
For insert process you can get the parent id by
$insertUser = User::create($userData);
then
$insertUser->id for taking the last insert id
And then to retrieve with respect to User_Exercise you shall use $this->belongsToMany from your User Model
Example
Have this in your User Model
public function getUser() {
return $this->belongsToMany('App\User', 'excercise_name', 'user_id', 'excercise_id')->select(array('exercise.id', 'excercise.name'));
}
And Get the data you need from any Controller like this
$userData = User::find($userId)->getUser;
I new in yii framework. i create an application in yii framework. i created model, controller, views using gii. After that i alter database table. I deleted 2 column and add 3 new columns. After that overwrite the model using the gii. But when i am trying to save into that table it show property(which was old column that I deleted) is not defined. Plz provide me a solution for this.
You need to define all columns in the validation rules() method in your model, have a look and make sure that you have defined a rule for every column in the table there, for example (if it's a string with max length 128):
public function rules()
{
return array(
...
array('myField', 'length', 'max'=>128),
...
);
}
See some info about validation rules.
Also, for forms if you're using CActiveForm widget and calling fields like so:
echo $form->labelEx($model,'myField');
echo $form->textField($model,'myField');
Then you'll need to make sure that a label is defined in the model too, in the attributeLabels() method, for example:
public function attributeLabels()
{
return array(
...
'myField'=>'My Field',
...
);
}
Lastly, if you want the field to be searchable, you'll need to add a statement to the search() method in the model, for example:
public function search()
{
...
$criteria->compare('myField',$this->myField);
...
}
Make sure you have all of those elements present and you shouldn't get the '* is not defined' error.
Also, if you're using schema caching in your main config file, you'll have to clear your cache before the app will see your new database structure.
Your changes should also be set at the Views since there are forms, widgets using the old properties !! (for this exact save issue, you will need to fix _form.php which is the partial responsible from your model Save & Update actions.
You can either do the same as you did with the model: (regenerate it using gii) or you can edit it manually (i recommend you get used to this since in the future you will have code you don't want to loose just because of altering a column name. simple Find & edit in most of the text editors will do the job).
May be you need to read a bit more about how MVC works in general & in Yii in special
This is because you are using schema-cache. Your table schema is cached in Yii. You need to flush AR cache. Either flush full schema cache or use
Yii::app()->db->schema->getTable('tablename', true); in start of your action. This will update model schema-cache.