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();
}
Related
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.
I'm new to Doctrine, and I just could not find a way to get the total number of results when using limit with Criteria (via setMaxResults function) in the EntityRepository::matching method.
In my repository (not an extend of EntityRepository), I'm using the following (I know this is not the optimal code, it is used just to learn Doctrine):
public function getAll($query = null) {
if ($query instanceof Criteria) {
$users = $this->em->getRepository('App\Entities\User')->matching($query)->toArray();
} else {
$users = $this->em->getRepository('App\Entities\User')->findAll();
}
return $users;
}
Now lets say that the Criteria is defined like so:
$query = Criteria::create();
$query->where(Criteria::expr()->contains('username', 'ron'));
$query->setMaxResults(10);
And there are actually more than 10 users that match that.
How can I get the total number of the users that match the criteria?
If you set maxResults to 10, you get 10 results ;).
Why don't you call getAll() to get all results and apply the MaxResults later?
//search for Ron's
$query = Criteria::create();
$query->where(Criteria::expr()->contains('username', 'ron'));
//check how many Ron's your database can find
$count = $repo->getAll($query)->count();
//get the first 10 records
$query->setMaxResults(10);
$users = $repo->getAll($query);
I want to limit related records from
$categories = Category::with('exams')->get();
this will get me exams from all categories but what i would like is to get 5 exams from one category and for each category.
Category Model
public function Exams() {
return $this->hasMany('Exam');
}
Exam Model
public function category () {
return $this->belongsTo('Category');
}
I have tried couple of things but couldnt get it to work
First what i found is something like this
$categories = Category::with(['exams' => function($exams){
$exams->limit(5);
}])->get();
But the problem with this is it will only get me 5 records from all categories. Also i have tried to add limit to Category model
public function Exams() {
return $this->hasMany('Exam')->limit(5);
}
But this doesnt do anything and returns as tough it didnt have limit 5.
So is there a way i could do this with Eloquent or should i simply load everything (would like to pass on that) and use break with foreach?
There is no way to do this using Eloquent's eager loading. The options you have are:
Fetch categories with all examps and take only 5 exams for each of them:
$categories = Category::with('exams')->get()->map(function($category) {
$category->exams = $category->exams->take(5);
return $category;
});
It should be ok, as long as you do not have too much exam data in your database - "too much" will vary between projects, just best try and see if it's fast enough for you.
Fetch only categories and then fetch 5 exams for each of them with $category->exams. This will result in more queries being executed - one additional query per fetched category.
I just insert small logic inside it which is working for me.
$categories = Category::with('exams');
Step 1: I count the records which are coming in response
$totalRecordCount = $categories->count()
Step 2: Pass total count inside the with function
$categories->with([
'exams' => function($query) use($totalRecordCount){
$query->take(5*$totalRecordCount);
}
])
Step 3: Now you can retrieve the result as per requirement
$categories->get();
I know this is weird, but somehow I would like to know if it is possible, I am creating my own pagination, and I have been looking for eager loading with pagination but some say it is still not available.
//this query will get the record count
$qry_counter = LeadsModel::with('emails','contacts','create_by_name','sources');
//this query will get the data
$query = LeadsModel::with('emails','contacts','create_by_name','sources');
if(isset($inputs['page_no']) && isset($inputs['records_per_page']))
{
//setting pagination variables
$paginate_start_record = ($inputs['page_no'] - 1) * $inputs['records_per_page'];
//get the corresponding results based on the page and records per page
$query->skip($paginate_start_record)->take($inputs['records_per_page']);
}
//loop through condition
foreach($conditions as $condition)
{
$query->orWhere($condition[0],$condition[1],$condition[2]);
$qry_counter->orWhere($condition[0],$condition[1],$condition[2]);
}
$results_counter = $qry_counter->get()->count();
$results = $query->get();
Any advise on how to optimize this code? Will it be possible to get the total records count first and then it will also return the records based on the set with skip and take? Thanks in advance
I'm having issues getting a proper count total with my Laravel model.
Model Structure
User
Item
ItemLike
A user can have multiple Items, and each of these Items can have multiple ItemLikes (when a user 'likes' the item).
I can easily get the individual ItemLike counts when using an Item model:
return $this->itemLikes()->count();
But I can't figure out how to get the total # of ItemLike's a User has across all the Item's he owns.
EXAMPLE
User A has 3 Items. Each Item has 5 ItemLike's, for a grand total of 15.
I tried using eager loading on the User model like this:
return $this->items()->with('itemlikes')->get()->count();
But that returns 3 (the # of Items)
These are the queries it ran, which appears like the second query is the one I want, yet every way I try it I still get 3 instead of 15
select * from `items` where `items`.`user_id` = '1000'
select * from `item_likes` where `item_likes`.`item_id` in ('1000', '1001', '1002')
After suggestions from others I found 2 solutions to get the result.
Using whereIn:
$itemViewCount = ItemView::
whereIn('item_views.item_id', $this->items()->lists('id'))
->count();
return $itemViewCount;
2 queries for a total of 410μs
Using join:
$itemViewCount = $this->items()
->join('item_views', 'item_views.item_id', '=', 'items.id')
->count();
return $itemViewCount;
2 queries for a total of 600μs
Isn't it just a case of creating a method that would return the number of items for the model. e.g.:
#UserModel
public function nbLikes()
{
$nbLikes = 0;
foreach($this->items() as $item) {
$nbLikes += $item->itemLikes()->count();
}
return $nbLikes;
}
And then User::nbLikes() should return the piece of data you are looking for?
try this:
$query="select count(il.id) from item_likes il,item itm where il.item_id=itm.id and tm.user_id=1000";