Eloquent and Pivot Tables in Laravel 4 - php

I have a Poll table, a Students table, and a pivot table between them that includes a token and their three votes.
public function students()
{
return $this->belongsToMany('Student', 'polls_students')->withPivot('token','first','second','third');
}
While working out saving the poll results, I came across some odd behavior that I don't quite understand. I'm hoping somebody can explain what it is I'm missing:
$poll = Poll::find(Input::get('poll_id'));
foreach($poll->students()->where('students.id', '=', Input::get('student_id'))->get() as $student){
var_dump($student->pivot->token);
}
$student = $poll->students()->where('students.id', '=', Input::get('student_id'))->get();
var_dump($student->pivot->token);
In the above code, the foreach loop will successfully display the token, where the second one throws the exception Undefined property: Illuminate\Database\Eloquent\Collection::$pivot
What am I missing? Are these two calls not logically creating the same object? How is 'pivot' working on the first and not the latter?

You first example:
$poll = Poll::find(Input::get('poll_id'));
foreach($poll->students()->where('students.id', '=', Input::get('student_id'))->get() as $student){
var_dump($student->pivot->token);
}
Here $poll->students() retrieves a collection and because of foreach loop you get a single object in your $student variable and you can use $student->pivot->token
You second example:
$student = $poll->students()->where('students.id', '=', Input::get('student_id'))->get();
var_dump($student->pivot->token);
Here you are doing same thing, using $poll->students() you are getting a collection but this time you are not using a loop and trying to do same thing using $student->pivot->token but it's not working because you didn't define any index from which you want to get the pivot->token, if you try something like this
$student->first()->pivot->token
Or maybe
$student->get(1)->pivot->token
Or maybe you can use first() instead of get() like this
$student = $poll->students()->where('students.id', '=', Input::get('student_id'))->first();
Then you can use
$student->pivot->token
Remember that, get() returns a collection even if there is only one record/model.

$poll = Poll::find(Input::get('poll_id'));
foreach($poll->students as $student){
var_dump($student->pivot->where('student_id',$student->id)->where('poll_id',$poll->id)->first()->token);
}

Related

How to select only one Column with scope of eloquent model?

I got two tables. Both have a relationship to each other. I´m trying to query both to get the matching results. This results get checked if they also have an column which matches with a parameter value.
I´m trying it with a scope and it work. I only need one column from the second table and I´m trying to use it as column in my first table when I got my result.
So the code works and I got an result but I´m trying to filter to select only one column from the second table.
My code look like that.
My controller:
public function test()
{
$UID='LQuupgYvnuVzbEoguY4TF8bnHUU2';
$event=Events::withState($UID)->get();
echo $event;
}
My model scope function:
public function scopeWithState($query,$UID){
return $query->with(['EventLiked' => function($query) use($UID) {
$query
->where('EventLiked.UID', $UID)
;
}]);
}
My hasMany relationship function:
public function EventLiked()
{
return $this->hasMany(EventLiked::class,'EID','ID')->select('State','UID','EID');
}
I would go for specifying columns inside closure.
New scope:
public function scopeWithState($query,$UID){
return $query->with(['EventLiked' => function($query) use($UID) {
$query
->where('EventLiked.UID', $UID)
->select('State');
}]);
}
Calling scope:
$event=Events::withState($UID)->get();
You're not getting expected results because Laravel splits it into 2 queries:
First, for selecting events.
Then it plucks EID
Second, when it looks for EventLiked where matching ID's is found (from second step) and loads as relationships.
So you want to change select statement only in 2nd query. Not in a first one

Laravel: how to access record by id in view

In my controller I return a view with a ?collection $programs? from an eloquent query to the view.
Controller
$programs = ScheduledProgram::where('registration_start_date', '<=', $today)
return View::make('admin/register_users/show', compact(programs));
I wan to do something like this without it running a new query from the view...
VIEW
{{$program->find(id)}}
I know that $programs is a dataset that already has the record, but I don't know the way to access the element by ID this way.
How do I do this?
(sorry, seems like an obviously searchable question but my search terms aren't comming up with the answer)
in that case you need to make a #foreach in $programs to access the data. Like this:
#foreach($programs as $key => $value)
{{$value->id}}
#endforeach
If the return is only one line you can do this:
{{$programs[0]->id}}
1.You have to add the method get (converts the "dataset " in a Laravel collection) to iterate the collection.
$programs = ScheduledProgram::where('registration_start_date', '<=', $today)
->get()
2.If you want to get a single record:
ScheduledProgram::where('registration_start_date', '<=', $today)->where('id', 5)->get();

How to get different queries (models) results with one statement? - laravel

Sorry if my title is confusing, not sure how to explain this within a line. Let's say I have a table with some columns and I have this
$model = Document::where('systemName', '=', $systemName)->where('ticketNumber', '=', ($nextTicketNumber))->get(); ticketNumber is unique where as there are quite a few systemNames
The above will get exactly what I want but I want more. I want an another array which will store all the rows under the same systemName. I know I can do this by doing
$allSystemNameModel = Document::where('systemName', '=', $systemName)
But is there a possible way to not having two variables and be easier?
No, you can't get both collections into one variable with one statement, however, you can create an array and store your results there:
$both = [];
$both['model'] = ...
$both['all'] = ...
UPDATE:
To avoid querying the database twice, you can use a first method that laravel provides us with.
$allSystemNameModel = Document::where('systemName', '=', $systemName);
$model = $allSystemNameModel->first(function ($doc) use ($nextTicketNumber) {
return $doc->ticketNumber == $nextTicketNumber;
});
$both['model'] = $model;
$both['all'] = $allSystemNameModel->all();
Note: Be sure to use use when working with php closures since $nextTicketNumber will be undefined otherwise.

How to Merge Two Eloquent Collections?

I have a questions table and a tags table. I want to fetch all questions from tags of a given question. So, for example, I may have the tags "Travel," "Trains" and "Culture" attached to a given question. I want to be able to fetch all questions for those three tags. The tricky, so it seems, is that questions and tags relationship is a many-to-many defined in Eloquent as belongsToMany.
I thought about trying to merge the questions Collections as below:
foreach ($question->tags as $tag) {
if (!isset($related)) {
$related = $tag->questions;
} else {
$related->merge($tag->questions);
}
}
It doesn't seem to work though. Doesn't seem to merge anything. Am I attempting this correctly? Also, is there perhaps a better way to fetch a row of rows in a many-to-many relationship in Eloquent?
The merge method returns the merged collection, it doesn't mutate the original collection, thus you need to do the following
$original = new Collection(['foo']);
$latest = new Collection(['bar']);
$merged = $original->merge($latest); // Contains foo and bar.
Applying the example to your code
$related = new Collection();
foreach ($question->tags as $tag)
{
$related = $related->merge($tag->questions);
}
The merge() method on the Collection does not modify the collection on which it was called. It returns a new collection with the new data merged in. You would need:
$related = $related->merge($tag->questions);
However, I think you're tackling the problem from the wrong angle.
Since you're looking for questions that meet a certain criteria, it would probably be easier to query in that manner. The has() and whereHas() methods are used to generate a query based on the existence of a related record.
If you were just looking for questions that have any tag, you would use the has() method. Since you're looking for questions with a specific tag, you would use the whereHas() to add the condition.
So, if you want all the questions that have at least one tag with either 'Travel', 'Trains', or 'Culture', your query would look like:
$questions = Question::whereHas('tags', function($q) {
$q->whereIn('name', ['Travel', 'Trains', 'Culture']);
})->get();
If you wanted all questions that had all three of those tags, your query would look like:
$questions = Question::whereHas('tags', function($q) {
$q->where('name', 'Travel');
})->whereHas('tags', function($q) {
$q->where('name', 'Trains');
})->whereHas('tags', function($q) {
$q->where('name', 'Culture');
})->get();
$users = User::all();
$associates = Associate::all();
$userAndAssociate = $users->merge($associates);
Merge two different eloquent collections into one and some objects happen to have the same id, one will overwrite the other. Use push() method instead or rethink your approach to the problem to avoid that.
Refer to web
Creating a new base collection for each eloquent collection the merge works for me.
$foo = collect(Foo::all());
$bar = collect(Bar::all());
$merged = $foo->merge($bar);
In this case don't have conflits by its primary keys.
I have faced some issue by using merge. So I used concat. You can used it like below.
$users = User::all();
$associates = Associate::all();
$userAndAssociate = $users->concat($associates);
All do not work for me on eloquent collections, laravel eloquent collections use the key from the items I think which causes merging issues, you need to get the first collection back as an array, put that into a fresh collection and then push the others into the new collection;
public function getFixturesAttribute()
{
$fixtures = collect( $this->homeFixtures->all() );
$this->awayFixtures->each( function( $fixture ) use ( $fixtures ) {
$fixtures->push( $fixture );
});
return $fixtures;
}
I'm sorry about that, but since PHP 7.4 you're available to do like this (better use merge).
$foo = Foo::all();
$bar = Bar::all();
/** $foo will contain $foo + $bar */
$foo->push(...$bar);
I would like to add that, i found that the concat method does not seem to override based on ID, while the merge method does. concat seems to work for me, while merge caused issues.

How to count and fetch data query in laravel?

How to merge this two queries ?
$data = DB::table('category_to_news')
->where('category_to_news.name', ucwords($category))
->remember(1440)
->count();
and
$data = DB::table('category_to_news')
->where('category_to_news.name', ucwords($category))
->remember(1440)
->get();
So, as far as I understand from your comment, you simply want to get all records from the table category_to_news and you want to know how many records are in there, right?
MySQL's count is an aggregate functions, which means: It takes a set of values, performs a calculation and returns a single value. If you put it into your names-query, you get the same value in each record. I'm not sure if that has anything to do with 'optimization'.
As already said, you simply run your query as usual:
$data = DB::table('category_to_news')
->where('name', ucwords($category))
->remember(1440)
->get(['title']);
$data is now of type Illuminate\Support\Collection which provides handy functions for collections, and one them is count() (not to be confused with the above mentioned aggregate function - you're back in PHP again, not MySQL).
So $data->count() gives you the number of items in the collection (which pretty much is an array on steroids) without even hitting the database.
Hi DB class dont return collection object it give error "call member function on array" but eloquent return collection object. for above code we can use collect helper function to make it collection instance then use count and other collection methods https://laravel.com/docs/5.1/collections#available-methods .
$data = DB::table('category_to_news')
->where('name', ucwords($category))
->remember(1440)
->get();
$data = collect($data);
$data->count();
You my get it using:
$data = DB::table('category_to_news')
->where('name', ucwords($category))
->remember(1440)
->get();
To get the count, try this:
$data->count();
Why you are using DB::table(...), instead you may use Eloquent model like this, create the model in your models directory:
class CategoryToNews extends Eloquent {
protected $table = 'category_to_news';
protected $primaryKey = 'id'; // if different than id then change it here
}
Now, you may easily use:
$data = CategoryToNews::whereName(ucwords($category))->get();
To get the count, use:
$data->count();

Categories