Can't eager-load a One-to-One relation in Laravel - php

In Laravel 5.2 I'm trying to eager load a User along with its Usersettings with this piece of code: App\User::with('usersettings')->get(), but it fails, and I can't seem to figure out why. This is the given error.
BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::usersettings()'
I've read the laravel docs and I've watched a lot of Laracasts, and this worked before, so I get the idea I'm missing something really small and probably obvious.
User.php
<?php
namespace App;
/* User with fields: id, email */
class User extends Illuminate\Database\Eloquent\Model {
protected $table = 'users';
public function usersettings() {
return $this->hasOne("App\Usersettings", "userid");
}
}
Usersettings.php
<?php
namespace App;
/* Settings with fields: id, userid, textcolor */
class Usersettings extends Illuminate\Database\Eloquent\Model {
protected $table = 'usersettings';
public function user() {
return $this->belongsTo('App\User', 'userid');
}
}
//Edit: I already tried lowercasing the s. This typo might have snuck in copying it to SO, but the error was there, and still is there, even after fixing it.
//Edit:
<?php
namespace App;
use App\UserSettings;
class User extends Illuminate\Database\Eloquent\Model {
protected $table = 'users';
public function settings() {
return $this->hasOne(UserSettings::class, "userid");
}
}
If I run php artisan tinker>>> App\User::with('settings')->get(), it works as expected, but below
<?php
namespace App;
use App\UserSettings;
class User extends Illuminate\Database\Eloquent\Model {
protected $table = 'users';
public function usersettings() {
return $this->hasOne(UserSettings::class, "userid");
}
}
gives BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::usersettings()' when I run php artisan tinker >>> App\User::with('usersettings')->get(). Likewise when I rename the method to abc and run php artisan tinker >>> App\User::with('abc')->get() (that fails as well)

You mistyped when defining the relation on the User model:
public function usersettings() {
return $this->hasOne("App\Usersettings", "userid");
}
I would suggest some cleanup:
name the tables 'users' and 'user_settings',
rename the field 'userid' to 'user_id',
rename the 'Usersettings' model to 'UserSettings'.
That way you dont need to explicitly define table names and foreign keys, because you follow the conventions and Laravel can "guess" those.
You can also rename the the relation 'usersettings()' to 'settings()', since its obvious that they're the users' settings. Then you can fetch it like: 'User::with('settings')->get()'.

Did you try running composer dump-autoload after you changed your model relationships?
Just to make sure you're aware of this: you don't actually need to define the relationship in your UserSettings model to achieve what you want, only define the relationship in the User model. This is because you'd only need to get the settings in the context of the User, not the other way around.
Code for the User model:
<?php
namespace App;
use App\UserSettings;
class User extends Illuminate\Database\Eloquent\Model {
public function userSettings() {
return $this->hasOne(UserSettings::class);
}
}
UserSettings model: make sure it's camel case and that userid is present in your migration (the convention in your case would be to have user_id, so I would rename that column in your migration because then Eloquent will pick it up automatically and you don't need the second parameter in your User model's hasOne() definition).
Also, rename your table to user_settings if you can. That's another Eloquent convention, this one being that the model name UserSettings translates the camel case S letter in the middle of the classname to _ (and as a matter of fact, you shouldn't even need to explicitly state the table name if you've used the name user_settings).
Code for the UserSettings model:
<?php
namespace App;
/* Settings with fields: id, userid, textcolor */
class UserSettings extends Illuminate\Database\Eloquent\Model {
protected $table = 'user_settings';
}
Now, you should be able to do the below action. Note that the with parameter needs to be the relation function name from the User model.
$usersThatHaveSettingsIncludedInTheResults = User::with('userSettings')->get();

Related

Trying to get property of non-object in laravel 5.3 app though model relations seems appropriate

User model
class User extends Authenticatable{
public function enrollments() {
return $this->hasMany('App\enrollments','user_email');
}
}
Batch model
class batch extends Model{
protected $table = 'batch';
public function enrollments() {
return $this->hasMany('App\enrollments');
}
}
Enrollments model
class enrollments extends Model{
public function batch() {
return $this->belongsTo('App\batch');
}
public function user() {
return $this->belongsTo('App\User','email');
}
}
if I use $enrollment->batch->title, it works..
but if I use $enrollment->user->name, it gives an error
Trying to get property of non-object
Please help, I am stuck
Thanks in advance
EDIT
The problem arose after I changed the foreign key from between user and enrollment from id to email and renamed my column to user_email from user_id. Before that code was working fine.
Solved
Got the problem, It was with some data in enrollment which didn't have registered email with user.
This code will note work simply because User is not a child of Model. To fix it you must extend from Model. Authenticatable is an interface and there is an equivalent trait; there is no such class.
You must implement the interface if you want your user class to be Authenticatable. But to answer your question, extend the base model, Model.
class User extends Model {
public function enrollments() {
return $this->hasMany('App\enrollments','user_email');
}}
In addition:
If you need to log an existing user instance into your application,
you may call the login method with the user instance. The given object
must be an implementation of the
Illuminate\Contracts\Auth\Authenticatable contract. Of course, the
App\User model included with Laravel already implements this
interface:
https://laravel.com/docs/5.3/authentication

Eloquent doesn't get the "belongsTo" item

I have the Project model and the Contract model. When i execute Project:all() it gets me only the projects without the contract, same for contract. I tried to dd() inside contract and doesn't do anything, like is never executed. I also tried with App\ prefix and without.
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
protected $table = 'project';
public function contract() {
return $this->belongsTo('Contract');
}
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Contract extends Model
{
protected $table = 'contract';
public function project() {
return $this->hasMany('Project', 'ContractID', 'ContractID');
}
}
I try to retrieve them like this:
$projects = Project::all()->take(10);
You have a few problems here.
Project::all()->take(10);
This only returns a collection of projects. You havent specified that you want the contracts also.
$projects = Project::with('contract')->get();
In your belongsTo - You havent specified the column that the table should join on. You need to do this, because you have not used a standard id for primary key and contract_id for foreign key.
unrelated to specific question, but your relationship in contract model is also wrong.
public function project() {
return $this->hasMany('Project', 'ContractID', 'ContractID');
}
If one contract has many projects, then your public function project() should be public function projects();
Finally - Why are you using non-standard table / column naming conventions? What's wrong with contract_id? Are you aware that mysql is non-case sensitive? Also the project table could be renamed projects and the contract table could be renamed contracts. It will make you writing your eloquent relations much easier and makes more sense!
If you used standard naming conventions, then you could just do this to declare your model relations.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Contract extends Model
{
public function projects() {
return $this->hasMany('Project');
}
}
Notice you dont need to specify the table name in the model, or how the table is related to the Project.

Calling Correct Database Table with Laravel all()

I am just getting started with Laravel 5. I am trying to set up a basic page that retrieves all data from a database table. I have a table call people and I have a controller called ContactController.php. The controller has the following code:
<?php namespace App\Http\Controllers;
use App\Contact;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ContactController extends Controller {
public function index()
{
$people = Contact::all();
return $people;
}
}
When I access my page, I get the following error:
QueryException in /home/vagrant/sites/laravel/vendor/laravel/framework/src/Illuminate/Database/Connection.php line 614:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'homestead.contacts' doesn't exist (SQL: select * from `contacts`)
Why is it trying to access homestead.contacts? My table is called people. I never created a table called contacts. Where is it drawing the table name from and how do I correct this?
Thanks.
Quote from the docs:
Note that we did not tell Eloquent which table to use for our User model. The lower-case, plural name of the class will be used as the table name unless another name is explicitly specified.
So if you define nothing else, Laravel will take the plural snake_case version of your class name:
Contact => contacts
FooBar => foo_bars
To fix your issue either change the model name accordingly. In this case Person will let Laravel search for the table people.
Or explicitly specify the table in your model:
class Contact extends Eloquent {
protected $table = 'people';
}
Do you have the right table name in your app/Contact.php model?
protected $table = 'people';
Or are you using the wrong model?
Add the following line to your Contact model (app/Contact.php).
protected $table = 'people';
This will work.

Yii: add prefix/postfix to model class naming

Is there any way I can get Yii to work with models that have a prefix or postfix in their class name?
For example, I have a table user, which corresponds to the model User. Now, I want this model to have a prefix, say, EmulatedUser. Is there any way to achieve that without renaming my table?
The table and class name don't have to be the same. You can override the tableName in your model:
<?php
class EmulatedUser extends CActiveRecord {
public function tableName() {
return 'user';
}
}

Laravel 4 Model only works if i take another name for the class and file

Hello i have a Controller:
class AddressController extends BaseController {
public function showIndex()
{
$address = Postcode::all();
return 'hello';
}
And a Model (which doesn't work):
class Postcode extends Eloquent {
protected $table = 'postcode';
}
But it only works with another name like:
class Kla extends Eloquent {
protected $table = 'postcode';
}
Anybody knows why?
You have named something else in the root namespace as Postcode. Most common this is the database migration. This is why database migrations generally should be a class name describing what it is doing, so in your case CreatePostcodeTable.
This is also why you should be using namespaces.
Just to be clear in-case you don't understand namespaces. You have 2 classes with the same name. The composer autoloader has grabbed the first one it finds (the one that isn't your model) and tried to use it. There is no static method all on the other class so you get an error (which you should have pasted in your question).

Categories