I have a many to many relationship that looks like so
batches -> batch_contributors -> contributors
I am running into an issue when attempting to paginate, I am doing the following:
$contributions = Batch::with('contributors')->where('id', '=' , $batchid)->paginate(10);
Here are samples from my parsed JSON (faker data)
parsed JSON
end of parsed JSON
My problem is the contributor records are listed under the single batch record, so pagination is seeing this and loading all 100 contributors on the first page instead of 10 per page.
How do I get pagination to look at the contributor level instead of the batch level?
You can just invert the call and get contributors where batchId is equal to the one u parse, something like this:
Contributor::with(['batches' => function($query) use ($batchId) {
$query->where('id', $batchId);
}])->paginate(10);
Try this:
Batch::with('contributors', function($query){
$query->take(10); // to take 10 contributors for all the 10 selected Batchs
})->where('id', '=' , $batchid)->paginate(10);
Laravel will get 10 contributors in this case, and try to map them into the batchs, So If those 10 contributors belong to one batch, the rest batchs won't have any contributors.
Related
I want to get data from database with range like 0 to 50 and 51 to 100, this is possible? if possible please talk me how to do that.
$users = User::all();
I am not able to get clearly what do you want. But I have provided some examples below that might work for you.
$users = DB::table('users')
->whereBetween('id', [1, 50])
->get();
Also try skip and take.
You may use the skip and take methods to limit the number of results returned from the query or to skip a given number of results in the query:
$users = DB::table('users')->skip(50)->take(50)->get();
Alternatively, you may use the limit and offset methods. These methods are functionally equivalent to the take and skip methods, respectively:
$users = DB::table('users')
->offset(50)
->limit(50)
->get();
Hope this helps!
There are a number of options available to you. If you're looking to obtain a working data set of x results at a time and not concerned about the id of the results, then one of the following might be of use:
Pagination
Chunking results
Both of the above will allow you to define the size/number of records returned for your data set.
If you're looking to return records between specific id values, then you'll want to use something like whereBetween on your database queries. The whereBetween clause can be used in conjunction with the pagination and chunking.
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
I have a model that is "static". Its contains games with various information about them. And users can comment on games with a polymorphic relationship. The problem is that I want to fetch the 10 latest games that has been commented on, and that user for that comment. So Game doesn't have a relationship to Users.
I have manage to solve it like this:
$games = Game::join('comments', 'games.id', '=', 'comments.commentable_id')
->where('comments.commentable_type', Game::class)
->latest('comments.created_at')
->groupBy('games.id')
->take(10)
->withCount('comments')
->get()->each(function($game){
$game->comment = $game->comments()->orderBy('created_at', 'desc')->first();
$game->user = User::find($game->comment->user_id);
return $game;
});
But this creates a lot of queries, I would like to eager load, don't have that N+1 problem everyone is talking about.
I got another solution to add this function to Game
class Game extends Model {
public function latestComment() {
return $this->morphOne(Comment::class, 'commentable')->latest();
}
}
And run the query like $games = Game::withCount('comments')->with(['latestComment.user'])->take(10)->get();
But it seems as this query doesn't fetch the users. Only the first user is fetched. And also the comments_count (withCount()) only returns a value for the firsts result.
So I'm stuck!
The expected result
Load the 10 latest games with the latest comment for that game, with the totalt count of comments for that game, with the user for the latest comment - everything eager loaded to avoid the n+1 problem.
Is it possible?
ps. prepared a sqlfiddle http://www.sqlfiddle.com/#!9/c07570
Edit
It seems as the second query in this question "takes" (take(10)) takes the first 10 games in from games table, regardless if they have comments or not. If I have 15 comments for games with IDs from 20-30, the query will check IDs from 1 to 10.
If I run the second query in my question, with just the fiddle data in my comment table. Result 10 will contain game with id of 10, no user, no comments_count etc.
You can use a modified withCount() to get the latest comments.created_at:
$games = Game::withCount(['comments',
'comments as latest_comment' => function($query) {
$query->select(DB::raw('max(created_at)'));
}])
->having('comments_count', '>', 0)
->orderByDesc('latest_comment')
->with('latestComment.user')
->take(10)
->get();
I would access directly the comments table/model, query for the last 10 comments, pluck the games ids, and use them to query the games model/table with a whereIn.
Also add to the games query a selectRaw('(SELECT COUNT(*) FROM comments where ...) as commentCount')
I have a Batch model, which hasMany Results and belongsTo a Project. The current status of a batch is based on the status of its most recent Result. So, in my batch model I have this:
public function allForProject($pid)
{
$batches = $this
->with(static::$relatedObjects)
->with('current_status')
->where('project_id', '=', $pid)
->get();
return $batches;
}
public function current_status()
{
return $this
->belongsToMany('BehatEditor\Models\Result')
->orderBy('created_at', 'DESC')
->limit(1)
;
}
...So by saying "->with('current_status')" I am trying to eager load only the most recent result for that batch - there may be thousands of them per batch that I do not want to return to the front end.
Now, this doesn't break, but the "limit(1)" actually seems to limit the number of Batches that get returned with a Result. Even though each one of my Batches has 2 results with my test data, when I use limit(1) only one of them comes back with any data. When I use limit(2), only 2 batches come back with a single current_status record (desired) but the rest have an empty array for current_status.
This is a Silex project using Eloquent as an ORM, so Laravel specific methods won't work.
Any help is much appreciated!
UPDATE:
It looks like Eloquent just doesn't support this. see http://irclogs.julien-c.fr/2013-12-19/01:48#log-52b25061a599aafb54008650. I would like to update my question to be how can I cleanly add the raw SQL I need to my query? Can I supply my own method that holds only the SQL needed, or do I need to replace all ORM usage in allForProject()?
Instead of ->limit(1), use ->first().
Update: Misread what you said, this should work.
I use Codeigniter with datamapper orm and have a problem
this are my models:
mailing -> has many row
row -> has many cell
cell -> has many version
version has one created and one updated field.
I want to get the last 10 mailings order by last version changes created or updated..
I thought to do it like this:
$versions = new Version();
now get last 10 versions order by created or updated
and distinct by mailing_id and now get all mailings to show...
like this: ?
foreach ($version as $v)
{
$v->mailing->get();
}
thx for helping
Yes, you can call ->get() on every related model inside a loop but this would generate a n+1 query scenario and be slow if you are looping over lots of version rows.
You can use the include_related to get full Mailing instances loaded with data when you query Versions in one step (with a join behind a curtain) like this:
$versions = new Version;
$versions->order_by(...)->limit(...); // add your ordering and limiting as before
$versions->include_related('mailing', null, true, true);
// include related mailings, with of their fields and create instances, see
$versions->get();
foreach ($versions as $version) {
// now the $version->mailing is a Mailing instance loaded with the related data
print $version->mailing->id
}