Laravel 4 - how to use a unique validation rule / unique columns with soft deletes? - laravel

Assume, you are trying to create a new user, with a User model ( using soft deletes ) having a unique rule for it's email address, but there exists a trashed user within the database.
When trying to validate the new user's data, you will get a validation error, because of the existing email.
I made some kind of extra validation within my Controllers, but wouldn't it be nice to have it all within the Model?
Would you suggest creating a custom validation rule?
As I haven't found a clean solution now, I am interessted in how others solved this problem.

You can validate passing extra conditions:
'unique:users,deleted_at,NULL'

This sounds like an issue with your business logic rather than a technical problem.
The purpose of a soft delete is to allow for the possibility that the soft-deleted record may be restored in the future. However, if your application needs uniqueness of email (which is completely normal), you wouldn't want to both create a new user with that email address and be able to restore the old one as this would contravene the uniqueness requirement.
So if there is a soft deleted record with the email address for which you are adding as a new record, you should consider instead restoring the original record and applying the new information to it as an update, rather than trying to circumvent the uniqueness check to create a new record.

This is the best approach
'email' => 'required|email|unique:users,email,NULL,id,deleted_at,NULL',
It give you this query
select count(*) as aggregate from `users` where `email` = ? and `deleted_at` is null

Laravel offers "Additional Where Clauses".
My url validation rule (from the update model method) looks like this:
$rules['url'] = 'required|unique:pages,url,'.$page->id.',id,deleted_at,NULL';
This means that the url must be unique, must ignore the current page and ignore the pages where deleted_at id not NULL.
Hope this helps.

Your Eloquent model should have the $softDeletes property set. If so, then when you perform a WHERE check, like User::where('username', 'jimbob'), Eloquent will automatically add in the query WHERE deleted_at IS NULL... which excludes soft deleted items.

In laravel 6.2+ you can use
'email' => ['required', Rule::unique('users')->whereNull('deleted_at')]

Related

Laravel get collection of created/updated rows

new to laravel.
My use case:
Update multiple rows (say: resources table).
Create multiple users (users table).
Retrieve ids of created users
What I currently did:
First, Update the resources table whereIn('id', [1,2,3,4]). (Update eloquent)
Second, Get array of the updated resources (Another eloquent: Resource::whereIn('id', [1,2,3,4])->get()->toArray())
Bulk create a users. Refers to the resources collection above. (Another eloquent: User::create($resources))
Lastly, get the ids of the created users (Not resolved yet. But I might have to use another eloquent query)
All in all, there are 4 Eloquent queries, which I want to avoid, because this might have performance issue.
What I wanted to do is that, On first step(Update), I should be able to get a collection of models of the affected rows (But I can't find any reference on how to achieve this). And then use this collection to create the users, and get the users ids in one query with User::create() so that I will have 2 queries in total.
There is no need to invent performance problems that do not exist.
Update or Insert return only count of affected rows. Commands Select, Insert, Update performed separately. This is a SQL issue, not Laravel.
For single inserts (if you want add one row) you can use insertGetId method of a model.
For example,
$id = User::insertGetId([
'email' => 'john#example.com',
'name' => 'john'
]);
But you get only ID of record. You need to run select to get full data of the row.
To save multiple records with one query you can use
DB::table('table_name')->insert($data);
Since this won't be an eloquent method, you should pass all the columns including created_at and updated_at.
I don't know what is the method name for update.

Why Laravel/Eloquent single object save() updates multiple records

I'm fetching a specific record with a DB table using
$myTableObj = MyTable::where(['type' => $sometype])->first();
Getting it successfully, updating some fields and saving with
$myTableObj->save();
Surprisingly, this record is updated along with another record that also has 'type' = $sometype. What can be done to prevent this?
NOTE: originally the table did not have the auto increment id field, but I have read in forums that it may make problems in Laravel so I did add it, which did not solve the problem.
Method save() working with 'id' filed only.
You can try this
$myTableObj = MyTable::where(['type' => $sometype])->update(['something' => 'value']);
Source
I understand update() is to update, but, my answer works fine and fits good for update too. Its useful where you dont want columns to be defined once again for update, (sp when they are not fillable, its tested with primary key as condition)
$myTableObj->save(); basically its for saving new record, if you want to update that row you can update like below code:
$myTableObj=new MyTable;
$myTableObj->exists=true;
$myTableObj->type=$sometype;//this is your condition, identify
$myTableObj->update();
I think what's happening here is Laravel is saving as well as updating row.

Algolia Update Index On Relational Database change with Laravel Scout

I have implemented Aloglia for my Movies table with actors as relational table and it works fine.
Problem:
When I update any movie its also updating algolia index (its good). But how can I update index if I made any change in relational table (for example update an actor of movie).
How to push a specific record manually with laravel scout.
Thanks
The issue itself lies in laravel's events. Whats happening is scout is listening for an 'updated' event which only occurs in laravel when the model object is saved and is dirty (aka value differ from that in the db).
There are two ways you can do this.
The bad lazy way would simply be to add ->touch() to the model prior to save - this will force the updated_at field to update and ultimately trigger the updated event. This is bad as you're wasting a DB query.
The second and preferable way is to register another observer on 'saved' which triggers regardless of whether or not the object is dirty. Likely you either want to check if the model is dirty and only index when its not (to prevent double indexing from the updated event) or just de-register the 'updated' listener that comes in Scout.

Using two different slugs on a route

I'm upgrading an old procedural site to laravel 5.2, and I'm struggling with the old routes I made.
On this website, the routes were made like this : {user_slug}/{content_slug}.html. For the moment, I use cviebrock/eloquent-sluggable to generate the slugs, but I'm open to another one if this one cannot meet my needs.
I have two questions :
Can I make the content-slug unique, but per user ?
How can I write the route and the controller in order to match the correct user slug ad the correct content slug ?
I have not done this myself but I believe there would be a way in the validation rules to do this. Here is an untested rough draft to check content_slug in the posts table but only check uniqueness where the user_id field equals a variable:
'content_slug' => "unique:posts,content_slug,NULL,id,user_id,$user->id"
Depending on who you ask, they may advise you (either instead of or as well as doing the above) to set up a key in the database based on the user_id and content_slug fields. This way the database returns an error if an insert is attempted as well as gives a performance boost when running a query off that index. Queries off of an index can literally give an exponential performance increase.

Storing remember_token to a separate table than users?

is it possible to set another table & column to store my remember tokens? I know that the framework tries to automatically find a remember_token column in my "users" model, but I want to store it separately from users. Is there a way to configure my default tokens table? Thank you
P.S - I'm using laravel 5
First, you need to create separate model for storing remember tokens and define a relationship on your User model like so
public function rememberToken() {
return $this->hasOne('RememberToken');
}
Then you need to override methods on your User model, originally defined in Authenticatable trait. Override getRememberToken() and setRememberToken() methods. You will also need to override getRememberTokenName() as it is used in where clause in EloquentUserProvider::retrieveByToken() see EloquentUserProvider line 60. In order for this to work properly you probably have to add global scope to your User model to join remember_tokens table on every query, and return 'remember_tokens.token' from getRememberTokenName() method.
Think twice as it seems more trouble than it is worth. Why would you want to store your tokens separately anyway?
I believe the way Laravel works uses a seperate column in the users table to store a single remember_me token. From my understanding it seems that logging out resets this token regardless of storing the token in a cookie (correct me if I'm wrong).
https://github.com/laravel/ideas/issues/971
If you log in with remember_me checked on your personal computer,
then again on your phone,
and maybe again with any other device,
then finally the act of signing out on any device using the remember_me token or not will reset this token in the DB.
If Laravel had a separate table, able to remember each device, it might solve the problem.

Resources