How to show all data where date is earlier than today laravel - laravel

I`ve got code that shows all of the placements for a user. Is there a way to narrow this down to only show placements that are in the future? I've tried to do this using where and carbon::now to no avail.
My current code to show all of the placements :
$placements = Auth::user()->placementsAuthored;
$placements->load('keystage', 'subject', 'dates');
Placements Authored connection to connect a user to a placement :
public function placementsAuthored()
{
return $this->hasMany(Placement::class, 'author_id');
}
My attempt at trying to do this. I get no errors but the code doesn't work. It doesn't seem to take any effect of my where clause any ideas?
$placements ->where('date','>',Carbon::now()->format('Y-m-d'));

After a bit of tweaking, I found that this works but I don't understand why this works and the above doesn't. In my mind, they do the same but this is a longer way of doing it. Any idea why this works and the above doesn't?
// Only load future placements
$placements = Placement::whereHas( 'dates',
function ($q) {
$user_id = Auth::user()->id;
$q->where('author_id', $user_id)->where('date','>=' ,Carbon::now()->format('Y-m-d'));})->get();

You should take a different name for the date column because the date keyword is already reserved in the PHP function this is not a standard way

Related

Exception when testing CursorPaginator

I am running to an issue where the following code fails in testing only, while behaving correctly in the browser.
The code should provide a CursorPaginator for an Eloquent collection and return it as JSON.
Test Code:
public function testMoreNotesAreReturnedIfRequested()
{
$item = Item::factory()->has(Entry::factory()->for($this->user)->count(6))->create();
// Get page one of paginator, and request page 2 using the URL it returns
$response = $this->actingAs($this->user)->json('get',"/items/{$item->id}/notes/more");
$next = $this->actingas($this->user)->json('get',$response->decodeResponseJson()['next_page_url']);
$next->assertJson(fn (AssertableJson $json) => $json
->has('data')
->has('paginator'));
}
The above fails with the following exception:
Only arrays and objects are supported when cursor paginating items.
I have confirmed that there are 6 entries created in line 1, and that they all have different millisecond creation times as suggested when googling the problem.
Any ideas?
Controller Code:
return $item->entries()
->latest()
->with(['content','user'])
->cursorPaginate(5)
->withPath("/items/$item->id/notes/more")
->through( static function ($item) use ($request) {
$item->setAttribute('can_edit',$request->user()->can('update',$item->content));
$item->setAttribute('can_delete',$request->user()->can('delete',$item->content));
return $item;
});
Edit - the test fails every time, whereas the browser only fails when the number of items in the collection is 6, 7, 16 or 17. I am now even more confused.
This seems to be caused by using latest() for ordering. The problem was resolved by changing to
->orderBy('id','desc')
Related issues that people are having seem to suggest there is a problem with the precision of datestamps (in my case MariaDB providing 6 decimal places after the second). See here and here.
I wonder if this is a bug in CursorPaginator.

Get availability of formers for a sitting

I have two forms; the first is the form formers with two fields (name, firstname).
I also have the form trainings with two fields (date_sitting, fk_former).
My problem, if I want to add the other sitting today (07/07/2019), I would like to see only the formers who have no training today.
Here, I retrieve a former who has a sitting today.
Do you think it's possible to get only the formers who have no of sitting for now?
Edit: 10/07/2019
Controller Training
public function index()
{
$trainings = Training::oldest()->paginate(5);
$formersNoTrainingToday = Training::whereDate('date_sitting', "!=", Carbon::today())
->orWhere('date_sitting', null)->get();
return view('admin.trainings.index', compact('trainings', 'formersNoTrainingToday'))
->with('i', (request()->input('page',1) -1)*5);
}
And
public function create()
{
$formers = Former::all();
return view('admin.trainings.create', compact('formers','trainings'));
}
I would like to see only the formers who have no training today.
Sure - you can determine your correct list of candidates to show by using the following query:
$formersNoTrainingToday = Training::whereDate('date_sitting', "!=", Carbon::today())
->orWhere('date_sitting', null)->get();
This should work... but it assumes a few things within your code / db. If this fails, consider a few options to replace the whereDate section above:
Using where:
->where('date_sitting', '!=', \Carbon::today()->toDateString())
Using formatted date if that column on the DB is a different format than Carbon:
->whereDate('date_sitting', "!=", Carbon::now()->format('m/d/Y'))
If you're not using Carbon for some reason, you can try the raw query route for today:
->whereDate('date_sitting', "!=", DB::raw('CURDATE()'))
Bottom line, here are a number of ways to get close to this. But you may need to tweak this on your own to suit your needs. You may need to take Timezone or some hours of difference into account, so you may need to add a range or buffer. But the above should get you close if not all the way there.
HTH

date calculation in laravel model

I want to return values in my model based on dates but not sure how to?
Logic
calculate the date that post has published + 7 days after that and return true or false.
example
Let say I want add new label in posts from the date that post published till 7 days after that, and in the day 8th that new label be removed.
How do I do that?
Update
I ended up with something like this in my model currently
public function isNew($query){
return $query->where('created_at', '<=', Carbon::now()->toDateTimeString());
}
but it returns Undefined variable: isNew in my blade.
please give me your suggestions.
Are labels stored in a separate table from your posts? You could use 'effective dating'. Have indexed 'show_at' and 'hide_at' fields in your labels table. Have entries there get created as soon as the post does. You could use database triggers to do this, or model observers. And when querying for all labels related to a given post, make sure they're between those dates (or those dates are null).
For a more simple solution, you could do a whereRaw('CURRENT_DATE <= DATE_ADD(dateFieldName, INTERVAL 7 DAY)'). If timezones are a factor, you could use the CONVERT_TZ() function, though it's generally good practice to store everything in UTC to avoid those issues.
I would recommend you make use of Carbon:
https://carbon.nesbot.com/docs/#api-difference
That link should help a lot but to point you in the correct direction, you should be able to do a comparison:
I assume you DB date is held in ISO (Y-M-D, eg: 2019-01-07)
$date= Carbon::createFromFormat('Y-m-d', $dateFromDB);
if($date->diffInDays(Carbon::now() < 8){
Code for Add Label here
}
Or if you are using timestamps:
$date= Carbon::createFromTimestamp($dateFromDB);
if($date->diffInDays(Carbon::now() < 8){
Code for Add Label here
}
Solved
thanks for all the helps I solved my issue with code below:
public function getisNewAttribute(){
$now = Carbon::now();
return Carbon::parse($now) <= Carbon::parse($this->created_at)->addDays(7);
}

Laravel - Collection with relations take a lot of time

We are developing an API with LUMEN.
Today we had a confused problem with getting the collection of our "TimeLog"-model.
We just wanted to get all time logs with additional informationen from the board model and task model.
In one row of time log we had a board_id and a task_id. It is a 1:1 relation on both.
This was our first code for getting the whole data. This took a lot of time and sometimes we got a timeout:
BillingController.php
public function byYear() {
$timeLog = TimeLog::get();
$resp = array();
foreach($timeLog->toArray() as $key => $value) {
if(($timeLog[$key]->board_id && $timeLog[$key]->task_id) > 0 ) {
array_push($resp, array(
'board_title' => isset($timeLog[$key]->board->title) ? $timeLog[$key]->board->title : null,
'task_title' => isset($timeLog[$key]->task->title) ? $timeLog[$key]->task->title : null,
'id' => $timeLog[$key]->id
));
}
}
return response()->json($resp);
}
The TimeLog.php where the relation has been made.
public function board()
{
return $this->belongsTo('App\Board', 'board_id', 'id');
}
public function task()
{
return $this->belongsTo('App\Task', 'task_id', 'id');
}
Our new way is like this:
BillingController.php
public function byYear() {
$timeLog = TimeLog::
join('oc_boards', 'oc_boards.id', '=', 'oc_time_logs.board_id')
->join('oc_tasks', 'oc_tasks.id', '=', 'oc_time_logs.task_id')
->join('oc_users', 'oc_users.id', '=', 'oc_time_logs.user_id')
->select('oc_boards.title AS board_title', 'oc_tasks.title AS task_title','oc_time_logs.id','oc_time_logs.time_used_sec','oc_users.id AS user_id')
->getQuery()
->get();
return response()->json($timeLog);
}
We deleted the relation in TimeLog.php, cause we don't need it anymore. Now we have a load time about 1 sec, which is fine!
There are about 20k entries in the time log table.
My questions are:
Why is the first method out of range (what causes the timeout?)
What does getQuery(); exactly do?
If you need more information just ask me.
--First Question--
One of the issues you might be facing is having all those huge amount of data in memory, i.e:
$timeLog = TimeLog::get();
This is already enormous. Then when you are trying to convert the collection to array:
There is a loop through the collection.
Using the $timeLog->toArray() while initializing the loop based on my understanding is not efficient (I might not be entirely correct about this though)
Thousands of queries are made to retrieve the related models
So what I would propose are five methods (one which saves you from hundreds of query), and the last which is efficient in returning the result as customized:
Since you have many data, then chunk the result ref: Laravel chunk so you have this instead:
$timeLog = TimeLog::chunk(1000, function($logs){
foreach ($logs as $log) {
// Do the stuff here
}
});
Other way is using cursor (runs only one query where the conditions match) the internal operation of cursor as understood is using Generators.
foreach (TimeLog::where([['board_id','>',0],['task_id', '>', 0]])->cursor() as $timelog) {
//do the other stuffs here
}
This looks like the first but instead you have already narrowed your query down to what you need:
TimeLog::where([['board_id','>',0],['task_id', '>', 0]])->get()
Eager Loading would already present the relationship you need on the fly but might lead to more data in memory too. So possibly the chunk method would make things more easier to manage (even though you eagerload related models)
TimeLog::with(['board','task'], function ($query) {
$query->where([['board_id','>',0],['task_id', '>', 0]]);
}])->get();
You can simply use Transformer
With transformer, you can load related model, in elegant, clean and more controlled methods even if the size is huge, and one greater benefit is you can transform the result without having to worry about how to loop round it
You can simply refer to this answer in order to perform a simple use of it. However incase you don't need to transform your response then you can take other options.
Although this might not entirely solve the problem, but because the main issues you face is based on memory management, so the above methods should be useful.
--Second question--
Based on Laravel API here You could see that:
It simply returns the underlying query builder instance. To my observation, it is not needed based on your example.
UPDATE
For question 1, since it seems you want to simply return the result as response, truthfully, its more efficient to paginate this result. Laravel offers pagination The easiest of which is SimplePaginate which is good. The only thing is that it makes some few more queries on the database, but keeps a check on the last index; I guess it uses cursor as well but not sure. I guess finally this might be more ideal, having:
return TimeLog::paginate(1000);
I have faced a similar problem. The main issue here is that Elloquent is really slow doing massive task cause it fetch all the results at the same time so the short answer would be to fetch it row by row using PDO fetch.
Short example:
$db = DB::connection()->getPdo();
$query_sql = TimeLog::join('oc_boards', 'oc_boards.id', '=', 'oc_time_logs.board_id')
->join('oc_tasks', 'oc_tasks.id', '=', 'oc_time_logs.task_id')
->join('oc_users', 'oc_users.id', '=', 'oc_time_logs.user_id')
->select('oc_boards.title AS board_title', 'oc_tasks.title AS task_title','oc_time_logs.id','oc_time_logs.time_used_sec','oc_users.id AS user_id')
->toSql();
$query = $db->prepare($query->sql);
$query->execute();
$logs = array();
while ($log = $query->fetch()) {
$log_filled = new TimeLog();
//fill your model and push it into an array to parse it to json in future
array_push($logs,$log_filled);
}
return response()->json($logs);

How can I retrieve the latest question of each thread in Propel 1.6?

I want to get the newest entries for each of my threads (private messaging system) with Propel 1.6 making use of the fluid ModelQuery interface. This would allow me to reuse both methods for getting newest entries and only getting entries where a user is involved (nobody wants to see messages not for him).
I already found out that in standard-SQL I have to use a subquery to get the newest entry for each of my forum threads. I also found out that in Propel you have to use a Criteria::CUSTOM query to achieve this, but the whole Criteria::CUSTOM stuff seems to be pre-Propel-1.6, because none of the examples makes use of the new ModelQuery.
Now the problem is, that I want to make use of the concenation feature in ModelQueries, where you can attach several own methods to each other like this:
$entries = MessageQuery::create()
->messagesInvolvingUser($user) // user retrieved or sent the message
->newestFromThread() // get the latest entry from a lot of Re:-stuff
I do not think that this would still be possible if I had to use
$c = new Criteria();
$c->add([the subquery filter]);
in newestFromThread().
What’s the best method to retrieve the latest entry for each thread given the following scheme (thread_id means that all messages belong to the same correspondence, I want only one entry per thread_id):
id(INT)
title(VARCHAR)
thread_id(INTEGER)
date(DATETIME)
The current PHP-implementation looks like this:
<?php
class MessageQuery extends BaseMessageQuery {
public function messagesInvolvingUser($user) {
return $this
->where('Message.AuthorId = ?', $user->getId())
->_or()
->where('Message.RecipientId = ?', $user->getId());
}
public function newestFromThread() {
return $this;
// To be implemented
}
}
And I am using it like this:
$messages = MessageQuery::create()
->messagesInvolvingUser(Zend_Auth::getInstance()->getIdentity())
->newestFromThread()
->find();
How about ordering results by date (DESC) and to limit to one result ?
Considering the answers in a similar question about pure SQL solutions, I guess it is easiest to add a new column newest indicating which message in a communcation is the newest. This probably fits the object-oriented approach of Propel better, too. I could write my application like this then:
public function preInsert(PropelPDO $con = null) {
$this->setNewest(1);
$this->getEarlier()->setNewest(0);
return true;
}

Resources