Laravel validator: complex rule for exists rule - laravel

I have a table that looks like this
vote_id
voter_email
target_id
vote_date
The voter can vote once per day for each target_id. I am trying to validate votes and this is what I've got so far
['email' => "exists:participants_votes,voter_email,target_id,$targetId,vote_date,$date"];
How can I check the date for a range? Is there a way to do it without writing custom code?
There is the after:date validator but it has to be in the same scope as exists:
Basically something like this: (this sql could be totally wrong though)
Select MAX(vote_date) FROM parcitipants_votes WHERE voter_email = ?
then I would check if this date is > now - 24 hours.
I'm trying to do it the "right" way by using validator but maybe this is not meant for it...
Note: I cannot just have 1 entry per email with a vote_count, because I need to be storing information such as the vote_date for each vote, even if its from the same person.

I decided not to use the validator for this complex scenario and to just query the database, it was simple and effective.
$voteDate = \DB::table('participants_votes')
->where('voter_email', '=', $user->email)
->where('contest_id', '=', $contestId)
->where('target_user_id', '=', $targetId)
->where('created_at', '>', new \DateTime('now -24 hours'))
->pluck('created_at');
voteDate would be null if the email has voted in the past 24 hours.

Related

Having a problem with Laravel Eloquent in query date format transformation

I am using laravel to make a query and return all results that match several criteria. One of them is by date, the problem is that I
store dates in the database in this format:
2021-03-26 12:20:00
and display them in this format:
March 26, 2021
It is a requirement for the project that if you search: March 26
All items with that given month and day are returned
This needs to be done in the query itself, because I also use pagination and other search and orderBy criteria
I am using this:
{...}
$attrDate = 'appointment.date';
$query->orWhereDate($attrDate, 'LIKE', ["%{$search}%"]);
$query->orderBy('appointment.date', $order)->take($pagination)->skip($skip);
$appointments=$query->get();
{...}
That works if I search for numbers cointained in the appointment dates (26, 2021...)
But I also want to be able to search by month in string format, like 'March 26' or 'March 26, 2021' and show appointments for that given date. There is where I get the errors when I try this:
$attrDate = 'appointment.date';
$date_transform= "DATE_FORMAT({$attrDate}, '%F%j,%Y')";
$query->orWhereDate($date_transform, 'LIKE', ["%{$search}%"]);
It returns a code 500 response with an sql error.
I don't know what to do, since all of this are requirements for the project and I can't really change them. I have searched in the Laravel documentation, but I have not found anything that can help me solve this. Thanks in advance.
Laravel have also eloquent methods whereDate / whereMonth / whereDay / whereYear / whereTime, so you can use any of them.
$users = DB::table('users')
->whereMonth('created_at', '12')
->get();
Source: Laravel Docs
I would personally recommend using carbon's parser like e.g.
$attrDate = 'appointment.date';
$dateSearch = Carbon::parse($search);
$query->orWhereDate($attrDate, $dateSearch);
$query->orderBy('appointment.date', $order)->take($pagination)->skip($skip);
$appointments=$query->get();
This offloads the issue to Carbon's date parsing (which is pretty good). The issue here is it will assume the year rather than searching for any year

Laravel how to query a pivot table created_at timestamp equal to a date

I have users and habits, and a habit_user table to join them.
I am querying like this:
$track = $h->userAnswers()->where('user_id', Auth::user()->id)->wherePivot('created_at', '=', Carbon\Carbon::now()->subDays($i))->first();
This is running in a loop that is counting back for 7 days. there is a record in the db that is created_at: 2018-10-23 04:48:44
In my habit model I have the method you'd expect:
public function userAnswers()
{
return $this->belongsToMany('App\Habit', 'habit_user_answers')->withTimestamps()->withPivot('answer_one', 'created_at')->orderBy('pivot_created_at', 'desc');
}
Why won't query get a record?
You are comparing the date time so only if both date and time is same, the query will throw a result.
You can compare dates like so:
wherePivot('created_at', '>=', Carbon\Carbon::now()->subDays($i)->startOfDay())->wherePivot('created_at', '<=', Carbon\Carbon::now()->subDays($i)->endOfDay())
First, I think you need to consider Laravel conventions about naming methods and properties.
I'll assume the following based on your structure that includes users and habits. So, we have a User model and a Habit model, a user belongsToMany habits and a habit belongsToMany users. Also the pivot table habit_user contains extra fields like answer_one, answer_created_at and timestamps.
If you want now to query the habits now you have two solutions:
1- using wherePivot()
auth()->user()->habits()->wherePivot('answer_created_at', today())->get();
auth()->user()->habits()->wherePivot('answer_one', '!=', 'something')->get();
2- using whereHas()
auth()->user()->whereHas('habits', function($query){
$query->where('pivot.answer_one', 'something');
})->get();

How to use whereBetween for dates in Laravel

I am trying to get the number of new users for each week using the created_at column. I am trying to use the whereBetweensyntax but it always return 0 even when it is suppose to return otherwise.
{{ DB::table('users')
->whereBetween('created_at', array(date("Y/m/d h:i:s", strtotime('sunday last week')), date("Y/m/d h:i:s", strtotime('saturday this week'))))->count(); }}
Any suggestions?
The query itself should work as written, though you might want to verify that created_at is a column of either timestamp, date, or datetime type. When you created the users table, did you use a migration to create your users table, and if so, did you specify $table->timestamps();? Or did you manually define the created_at column, and perhaps set it to a string?
A couple other (unrelated) suggestions:
• It appears that you're running this query in a view, echoing the count using blade. This logic would be better handled elsewhere, perhaps in your User model, and the result passed to the view by the controller.
• You can simplify your query using PHPs DateTime object, replacing your date(...strtotime...) with date_create(...):
$usersThisWeek = User::whereBetween(
'created_at', array(
date_create('sunday last week'),
date_create('saturday this week')
))->count();

Working with Eloquent, How to select popular comments

I am designer trying to learn coding here and Laravel is so great in that it enables design like me to be able to create something myself smile
On to my question, I followed some of tutorial and now I learn to build simple blog system.
So I have Post model that has relationship $this->morphMany('Like','likeable','likeable_type'); with Like model.
Everything works great so far. Now I want to be able to create 'Popular Posts' page that basically shows the posts ordered by popularity, which is number of likes in my case.
The 'likes' table has these field id | likable_id | likeable_type
'likeable_type', in this case, is 'Post'
'likeable_id' is the post id linked to id in 'posts' table
So in my controller, I tried this.
$popular_ids = DB::table('likes')
->select('likeable_id')
->groupBy('likeable_id')
->orderBy(DB::raw('COUNT(likeable_id)'), 'DESC')
->get();
$popular_posts = Post::whereIn('id',array_pluck($popular_ids,'likeable_id'))->paginate(15);
This gives me all the popular posts but not in the order that I want.
I'm not sure if there is better way to achieve this or it seems that I only miss another 'orderBy' method?
Any suggestion on this?
ps. Sorry for my poor English
SQL IN does not maintain order. To achieve this have to user ORDER BY FIELD('id', , ,...) to achieve this.
$ids = array_pluck($popular_ids,'likeable_id');
$raw = DB::raw('FIELD(id,' + implode(',', $ids) + ')');
$popular_posts = Post::whereIn('id', $ids)->orderBy($raw)->paginate(15);
For more information refer to,
Maintaining order in MySQL "IN" query
I would do this with a join. It's kinda complex but if you want to understand the internals you should read into LEFT JOIN and INNER JOIN. This example is particularly ugly because of the polymorphic relationship.
Post::select('posts.*', DB::raw('COUNT(DISTINCT likes.id) AS popularity'))
->leftJoin('likes', function ($join)
{
$join->on('likes.likeable_id', '=', 'posts.id')
->on('likes.likeable_type', '=', DB::raw("'Post'"));
})
->orderBy('popularity', 'DESC')
->paginate(15);
There is a bonus in that the popularity value is now available on the posts.
$post->popularity;
Thank you Collin for your time! Your answer leads me to the light!!! I modified your answer a bit and finally get what I need. I'm still trying to fully understand it though. This is what I ended up with.
Post::select('posts.*', DB::raw('COUNT(likes.likeable_id) AS popularity'))
->join('likes', function($join)
{
$join->on('likes.likeable_id', '=', 'posts.id')
->on('likes.likeable_type', '=', DB::raw("'Post'"));
})
->groupBy('likes.likeable_id')
->orderBy('popularity', 'DESC')
->paginate(15);
Ps. Even though this code gives me what I want, I'm not sure if I did my db design right.

Laravel 4: making a combination of values/columns unique

I'm importing a bunch of csv entries in my database with Laravel 4.
I can't really point at one column that has to be unique, it's a combination of 5 columns that makes it unique. However: how does one define this in Laravel?
Option 1: schema builder
You can use the $table->unique('email') method, but that only seems to allow one column, not a combination of columns.
Option 2: Validation
Less preferable, but I could validate the model before inserting it. However, again, using 'unique:[table]' validation rules, it will return an error when just one of the column values isn't unique, not a combination of them.
Can anyone tell me how I should go about this?
I'm sure I'm missing something, but I could use a push in the right direction :-)
Thanks,
Dieter
You can combine:
$table->unique( array('email','name') );
And pretty much everything in Laravel will accept arrays to do whatever you need to with 'more than one'.
Use Schema Builder's unique() method to define your data model, as Antonio mentioned.
Additionally, if you want to use validation on your model, consider my custom Validator rule for multiple UNIQUE indexes: https://github.com/felixkiss/uniquewith-validator
You can also do this;
$table->unique(["column1", "column2"], 'uq_columns');
Which means that you will have a unique column combination of all the columns i.e. column1 and column2
I know this question is for Laravel 4, but I just came across this on searches and found a solution for Laravel >= 5.3
Here it is:
Of course, the migration may look something like
$table->unique( array('email','name') );
Then to validate this, you do not need to use custom rules, just advanced rules:
'email' => Rule::unique('users')->where(function ($query) use ($request) {
return $query->where('name', $request->name);
}),
Of course, you may want to validate name before of this. The name should be required so that you may finish with something like this:
'name' => 'required|max:255',
'email' => Rule::unique('users')->where(function ($query) use ($request) {
return $query->where('name', $request->name);
}),
I hope it helps.
You can try this
$table->string("name");
$table->string("email")->unique("name")

Resources