Laravel model attribute with previous and next values - php

How can I get a model's attribute value with the previous and next maximum values?
Let's say I have a user model with a column for age. I now want to know the age of user x as well as the closest previous and subsequent age values.
$age = User::find($id)

In the code you posted, $age is currently a User instance, or null, so that doesn't do much.
If age is a column on your users table, then you could do something like this:
$age = User::findOrFail($id)->age;
try {
return User::where('age', '<', $age)
->orderBy('age', 'DESC')
->firstOrFail()
->age;
} catch (ModelNotFoundException $mnfe) {
return null; // Or similar
}
try {
return User::where('age', '>', $age)
->orderBy('age', 'ASC')
->firstOrFail()
->age;
} catch (ModelNotFoundException $mnfe) {
return null; // Or similar
}
Using $age (let's imagine 30) as your point of reference:
For Younger:
Query for anyone younger ('age' < 30)
Ordering them from 29 to 0 (DESC)
Find the first one (or failing if there are none)
Return that User's age
For Older:
Query for anyone older ('age' > 30)
Ordering them from 30 to ∞ (ASC)
Find the first one (or failing if there are none)
Return that User's age
Sidenote, this code can go in a Controller or Model as functions like getPreviousAge($age), getNextAge($age), etc.

Related

Laravel Eloquent get every nth of relation

I am querying a relation from a model, but since I want to limit the amount of rows that the relation returns to a set number (in this example 18), I've mapped the relation.
$sensors = Sensor::where('building_id', $request->building_id)->with('data_sensor')->get()->map(function($sensor) {
$sensor->setRelation('data_sensor', $sensor->data_sensor->sortByDesc('created_at')->take(18)->values());
return $sensor;
});
This works fine. However I am trying to get every nth row of my relation, in this example I want every 6th row.
I've tried the following (added whereRaw), but this gave an error that the whereRaw method doesn't exist.
$sensors = Sensor::where('building_id', $request->building_id)->with('data_sensor')->get()->map(function($sensor) {
$sensor->setRelation('data_sensor', $sensor->data_sensor->sortByDesc('created_at')->whereRaw( DB::raw('(`id`) % 6 != 0') )->take(18)->values());
return $sensor;
});
How can I get every nth of my relation, with a limit so it doesn't return all rows of the relation?
You are trying to use a querybuilder method in a collection, that's the reason for the error.
Try this:
$sensors = Sensor::where('building_id', $request->building_id)
->with('data_sensor')
->get()
->map(function($sensor) {
$sensor->setRelation(
'data_sensor',
$sensor->data_sensor->sortByDesc('created_at')->filter(function($s) { return $s->id % 6 != 0; })->take(18)->values());
return $sensor;
});

Eloquent Get higher value of a specific table's column

Using Eloquent in Laravel,
To get the minimum value of a column this code works:
public function getLowestYearBook()
{
return CV_Outputs::min('book_publication_year');
}
But to get the higher value it doesn't work, i'm using 'max' instead of 'min'.
How to get the higher? Thanks!
--------------- Edit:
the problem is I have some rows with "Not defined" text in it, so sorting by desc it returns that row, because letters are "higher" than number.
I fixed it by doing this:
public function getHighestYearBook()
{
return CV_Outputs::all()
->where('book_publication_year', '<>', "Not defined")
->sortByDesc('book_publication_year')
->first()->book_publication_year;
}
You should check if the type/value is the same in all rows, because max should do the job. But try sorting them in descending order which means highest first and then get the first element like this:
CV_Outputs::all()
->where('book_publication_year', '!=', 'Not defined')
->sortByDesc('book_publication_year')
->first();

Get recent items function with Doctrine

New to Doctrine. I'm trying to build a "list recent items function". It should get all records newer than 3 months but if the result is less than 50 records it should get older ones until 50 is fetched.
So if I have 100 records newer than three months I want to return all of them but nothing older. If I have 20 records from the last three months I want to return them and also the 30 most recent after that.
How would one do this with Doctrine? I don't want to make two queries (is it even called that?) if possible.
For now my EntityRepository just gets all records from the last three months as follows:
public function fetchRecent($from)
{
$criteria = new Criteria();
$criteria->where($criteria->expr()->gte('created', $from));
return $this->matching($criteria);
}
Edit
Hum, I read a bit too fast, you don't want to limit records if not older than three months. I don't see how to achieve this with a single query. (I'll think about it again but actually can't see..)
You could query the database to know how many recent records there are and then query the actual result.
public function fetchRecent($from, $nbRecords = 50)
{
$count = $this->createQueryBuilder('c')
->select('COUNT(c.id)')
->andWhere('c.created >= :from')->setParameter('from', $from)
->addOrderBy('c.created', 'desc');
$nbNewer = $count->getQuery()->getSingleScalarResult();
$query = $this->createQueryBuilder('q');
if ($nbNewer<$nbRecords) {
$query->setMaxResults($nbRecords);
} else {
$query->andWhere('q.created >= :from')->setParameter('from', $from);
}
$query->addOrderBy('q.created', 'desc');
return $query->getQuery()->getResult();
}

Laravel search where relationship does not exist

I can query where relationships exist by using the whereHas method, but now I need to get the inverse of this result, where the result does not match the result in the whereHas clause.
Here's my query:
$query->whereHas('actions', function ($query) {
$query->where('actions.created_at', '>', Carbon::now()->addDays(-30));
});
This gets things that are actioned in the last 30 days, but I need to get the things that are NOT actioned in the last 30 days.
It seems like I need to get the max(actions.created_at) from the relationship and see if that value is > 30 days ago, but I'm not sure how I can do this with eloquent.
Note: the relationship between person and action is 1 to many, so there could be multiple action records linked, so I can't just flip the operator to be a "<="
Remember whereHas has more than two parameters:
$query->whereHas('actions', function ($query) {
$query->where('actions.created_at', '>', Carbon::now()->addDays(-30));
}, '=',0);
As a matter of fact, it has two more, but by default it is set to '>=' and '1'. So we add the parameters '=' and '0' (or '<', and '1' for what it matters) to convert it in a subquery like 'all the actions that are not in the subset of actions added in less than 30 days).
whereHas method: http://laravel.com/api/4.1/Illuminate/Database/Eloquent/Builder.html#method_whereHas
Can't you just change your where query to a smaller or equal than?
$query->whereHas('actions', function ($query) {
$query->where('actions.created_at', '<=', Carbon::now()->addDays(-30));
});
Try this [Pseudo Code]:
$actions = App\Models\Action::all();
foreach($actions as $actionArray){
$actionCreatedAt = new Carbon($actionArray->created_at);
$now = Carbon::now();
$difference = $actionCreatedAt->diff($now)->days;
if($difference>30){
$exceedingThirty[] = $actionArray;
} else {
continue;
}
}
Then, you can use $exceedingThirty array, in your view.
OR
Try this:
$sql = "DATEDIFF(actions.created_at, '".date('Y-m-d')."' ) > ". 30;
return App\Models\Action::whereRaw( $sql )->get();
See, if that helps.

Return TRUE from a views_php filter is not working as expected

I have written a Global PHP filter, in a View, and I am expecting the returned value to be TRUE when a condition is met and when TRUE it should eliminate that row from my result set. The view displays its results in a table.
The filter code I have is as follows
global $user;
$uid = $user->uid;
$bid = db_query("SELECT entity_id FROM {field_data_field_bid_request_reference} WHERE field_bid_request_reference_line_item_id = '$row->line_item_id'");
foreach($bid as $value) {
$node_uid = db_query("SELECT uid FROM {node} WHERE nid = '$value->entity_id'")->fetchField();
if( $node_uid != $uid) {
return TRUE;
}
}
I am getting the results I expect as far as the if statement. The $node_uid is returning exactly want I want, the uid for whoever created the node with the nid = to the entity_id obtained from the db_query.
In my testing I have 12 rows, each with a line_item_id. There are 6 possible results for $node_uid and I am seeing them if I use a drupal_set_message to look at the 4 node_id inside the foreach. In my test case I have a uid of 5 and this should match 3 times. I should only see 3 rows in my final results.
The problem is that the 9 rows that should be eliminated because they match the if statement and should return TRUE are actually the rows displayed and the 3 that should fail the if statement test and should be displayed are the ones being removed from the output. This is almost as if the return value is reversed.
What ever I do it is as if the return value is incorrect when the if statement passes.
I would have expected the cases where the node_id to not equal the uid to return TRUE and these lines to not appear in my table.
Can anyone offer any suggestions about what could be going wrong and also about how to debug this filter?
When the filter displays TRUE, it filters out, AFAIK. So, you should probably have
if ($node_uid == $uid) {
return TRUE;
}
I'm pretty sure you can't use a foreach statement inside a views_php filter, only because the result of the filter is supposed to apply to one row and one row only. Kind of like "all or nothing".
I'd recommend editing the view and adding the the field Content: Author uid to the view, then adjusting your views_php filter like so:
global $user;
$uid = $user->uid;
$node_uid = $row->uid;
if ($node_uid != $uid) {
return TRUE;
}
Note: the variable $row->uid will not be available until after you add the Content: Author uid field to the view itself. And, you can optionally hide the value of the author uid from the view so no one sees it:
Hope that helps...

Categories