I have 2 models:
class User extends Eloquent
{
public function questions()
{
return $this->hasMany('Question');
}
}
class Question extends Eloquent {
public function users()
{
return $this->belongsTo('User');
}
}
In my questions controller, I have the following function:
public function index()
{
$questions = Question::with('users')->get();
return View::make('questions.index')->with('questions', $questions);
}
But when accessing this route, I get the following error:
'Trying to get property of non-object ...'
However, if I add the foreign key to the questions model like so:
public function users()
{
return $this->belongsTo('User', 'user_id');
}
Then everything works fine.
The thing is, I thought you only had to add the foreign key as the second parameter if it didn't follow the convention of using the model name followed by _id, which I did.
I know It works fine now but I'd like to know why I had to add the foreign key as the second parameter.
Thanks in advance.
Related
I am facing a super weird issue.
I have 3 tables and their equivalent model:
App\User
public function company() {
return $this->morphTo();
}
App\Supplier
public function user() {
return $this->morphOne(User::class, 'company');
}
App\Company
public function users() {
return $this->morphMany(User::class, 'company');
}
For some reason the relationship on App\Supplier does not work, but all the other works normal, I can even get the supplier if I have the user:
$supplier = \App\Supplier::find(1);
$company = \App\Company::find(2);
$supplieruser = \App\User::find(1);
$supplier->user //THIS RETURN NULL
$company->users //return collection of users normally
$suppplieruser->company //returns an instance of supplier model
I have tried, changing the name of the relationship and nothing.
Any idea?
In case someone find this in the future... the relationship was not working because I had in my AppServiceProvider the following:
Relation::morphMap([
.....
'supplier_to_supplier' => 'App\Supplier',
.....
]);
It seems that registering it here will affect all the morphs from that model.
So I am setting up a one to one relationship between MyModel and the users table.
MyModel obviously has a user_id column to tie back to the users.
However - when i go to setup the relationship in MyModel I have to set it up in a way which seems backward!
This is in MyModel:
public function user()
{
return $this->hasOne('App\User', 'id', 'user_id');
}
Why Am i having to set the opposite foreign and local keys... ? Am i missing something?
Do it like this
class MyModel {
public function user()
{
return $this->belongsTo('App\User');
}
}
class User {
public function myModel()
{
return $this->hasOne('App\MyModel');
}
}
And that should work as intended (one to one) relationship
I'm working on a Laravel-5.4 project. I've three tables users, articles and comments in my database.
Screenshot of articles table:
Screenshot of comments table:
Article model:
class Article extends Model
{
public function comments() {
return $this->morphMany('App\Comment', 'commentable');
}
}
Comment model:
class Comment extends Model
{
public function commentable() {
return $this->morphTo();
}
}
ArticleController contains the following method:
public function showComments() {
return Article::find(1)->comments;
}
Above showComments() method returns [] (empty array). I want to return all the comments of the article which has id=1. What is the problem?
The commentable_type column should store the model name fully namespaced, such as App\User. Did you enter this information in manually? Try changing it to App\User, App\Article etc and see if that works.
You can create a morphMap in the boot method of your AppServiceProvider to alias these namespaces to a more descriptive name, as you have done here.
public function boot()
{
Relation::morphMap([
'User' => 'App\User',
// etc
]);
}
To import Relation:
use Illuminate\Database\Eloquent\Relations\Relation;
Normal relationship methods don't usually have a condition, and tend to look like this:
class StripeCustomer extends Model
{
public function user()
{
return $this->belongsTo(User::class, 'stripe_customer_id');
}
}
In my model I have a condition in the relationship method like so:
class StripeCustomer extends Model
{
public function user()
{
if ($this->type === 'normal') {
return $this->hasOne(User::class, 'stripe_customer_id');
} else {
return $this->hasOne(User::class, 'stripe_customer_charity_id');
}
}
}
Does Laravel support conditional relationships in Eloquent like above. A lot of the usual methods still work like so:
StripeCustomer::get()->first()->user;
StripeCustomer::get()->first()->user()->get();
But would the following work predictably:
Foo::with('user')->get();
The issue here is that I am unsure in how the "with" operator works in Eloquent internally.
A reason I believe it also doesn't work is that the user() method needs to be executed for every model. However, when I added a dump(...) at the start of the method, I found it was only run once, indicating that with() does not work.
No, it won't work with with(). What do you think will happen when you try to execute the following code:
Foo::with('user')->get();
The answer is Laravel will create new instance of Foo and try to call user() to get the relationship object. This new instance doesn't have any type ((new Foo)->type will be null), therefore your method user() will always return $this->hasOne(Bar::class, 'b_id') and this relationship object will be used to construct a query.
As you can see this is clearly not what you wanted since only type B users will be eager loaded for all Foo rows. What you need to do in this case is create two relationships (one for each type) and accessors (get/set) for user:
class Foo extends Model
{
public function userA()
{
return $this->hasOne(Bar::class, 'a_id');
}
public function userB()
{
return $this->hasOne(Bar::class, 'b_id');
}
public function getUserAttribute()
{
if ($this->type === 'a') {
return $this->userA;
} else {
return $this->userB;
}
}
public function setUserAttribute($user)
{
if ($this->type === 'a') {
$this->userA()->associate($user);
} else {
$this->userB()->associate($user);
}
}
}
Then you can use with() for both relations to utilize eager loading:
$fooRows = Foo::with('userA', 'userB')->get();
...
foreach ($fooRows as $row) {
$row->user;
}
edit:
Since you've edited code in your question the example code in my answer no longer represents your case, but I hope you get the overall idea.
Yep, with() works. It runs a subquery on any relation your user() method returns. Since your relation already has a constraint, it applies said constraint to the subquery as you'd expect.
I have the following database tables:
users
id
name
seasons
id
name
teams
id
name
standings
id
season_d
team_id
user_id
fixtures
id
season_id
home_team_id
away_team_id
My question is, how would I get which user a team belongs to for a certain fixture? For example I may want to do the following:
$fixture = Fixture::find(1);
echo $fixture->homeTeam->user->name;
My models look like this:
Fixture Model
class Fixture extends Eloquent{
public function season(){
return $this->belongsTo('Season');
}
public function homeTeam(){
return $this->belongsTo('Team', 'home_team_id');
}
public function awayTeam(){
return $this->belongsTo('Team', 'away_team_id');
}
}
Team Model
class Team extends Eloquent{
public function standings(){
return $this->hasMany('Standing');
}
public function seasons(){
return $this->belongsToMany('Season', 'Standings');
}
public function users(){
return $this->belongsToMany('User', 'Standings');
}
}
Season Model
class Season extends Eloquent{
public function standings(){
return $this->hasMany('Standing');
}
public function teams(){
return $this->belongsToMany('Team', 'Standings');
}
public function users(){
return $this->belongsToMany('User', 'Standings');
}
public function fixtures(){
return $this->hasMany('Fixture');
}
}
I think I need to add a user function to the Team model instead of the current users function that's there, but I can't figure out the correct way to do the relationship. A team will only have one user for any given season. Any help would be appreciated, thanks!
UPDATE
I have added the following relationships to the Fixture model, which allows me to get the user and team through the standings table:
public function homeTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'home_team_id', 'team_id')->where('season_id', $this->season_id);
}
public function awayTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'away_team_id', 'team_id')->where('season_id', $this->season_id);
}
The problem with this is that I can't use it with eager loading, so there's quite a lot of queries running, as when I try to eager load them $this->season_id is null. Surely there's a better way?
I replaced the above with joins instead so that I can eager load which results in a lot less queries!
public function homeTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'home_team_id', 'team_id')
->join('fixtures', 'fixtures.season_id', '=', 'standings.season_id');
}
public function awayTeamStanding(){
return $this->belongsTo('App\Modules\Leagues\Models\Standing', 'away_team_id', 'team_id')
->join('fixtures', 'fixtures.season_id', '=', 'standings.season_id');
}