It's been a some time since I've programmed with Laravel and I'm stumped by the relations I need in order to create a foreign key -link with 2 models.
I have a database where there's a table "company" containing companies, and I also have a table called "projects", which contains projects.
So the Projects- table contains a column called "employercompany" with a foreign key constraint to the company-table.
I'm trying to print out the company's name in a project page in laravel with
{{$project->employercompany->name}}
But keep getting "Trying to get property of non-object"
My model pages look like this:
//Company
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
public function projects()
{
return $this->hasMany('App\Projects', 'employercompany', 'id');
}
}
and
// Projects
namespace App;
use Illuminate\Database\Eloquent\Model;
class Projects extends Model
{
public function employercompany()
{
return $this->belongsTo('App\Company');
}
}
I know this is an easy problem but I just can't wrap my head around it
*****EDIT*****
I found the solution. Thanks Radical for providing some insight.
I ended up changing the employercompany column to company_id and, all the others as such too.
After that I fiddled around what I'm guessing fixed the thing was that I changed my database search query from
DB::table('projects')->get();
into
Project::all();
Don't know if that was the change needed but it sure feels like it was.
When defining a belongsTo relation like you have done, Laravel will try and 'guess' the keys you are using based on the class name. In this case, it will look for a column company_id on your Projects model since your model is called Company.
Like you have done for the projects() method on your Company model, you should tell Laravel that you are using the employercompany column to reference the Company model:
public function employercompany()
{
return $this->belongsTo('App\Company', 'employercompany');
}
For more information, see the relevant documentation here.
In addition, to make things easier, it might be worthwhile to try - if possible - to adhere to what Laravel 'expects' your database columns to be called, so situations like this are resolved automatically.
It should be like
$project = Project::find($id); //id of project
$compnany_name = ($project->employercompany()->get())->name;
Related
I'm trying to use a HasMany relation in a HasOne.
I have following Models:
class Auction extends Model
{
//...
public function bids(): HasMany
{
return $this->hasMany(Bid::class, 'auction_id');
}
public function approvedBids(): HasMany
{
return $this->bids()->approved();
}
public function topBids(): HasMany
{
return $this->approvedBids()->orderByDesc('price')->take(10);
}
public function topBid(): HasOne
{
//return $this->topBids()->firstOfMany(); // Not Working
//return $this->hasOne(Bid:class, 'auction_id)->ofMany('price','max')->approved(); // not working
//return $this->hasOne(Bid:class, 'auction_id)->approved()->ofMany('price','max'); // not working
//return $this->hasOne(Bid::class, 'auction_id')->ofMany('price', 'max'); // working but not as I expecting
}
}
class Bid extends Model
{
//...
public function scopeApproved(Builder $query): Builder
{
return $query->where('state', BidState::STATE_APPROVED);
}
//...
}
As you can see in the source, I'm looking for a way to make a relation that retrieve the Top Bid (ONE BID) from topBids() relation, but I don't know how, and none of my approaches works:
$this->topBids()->firstOfMany(); // Not Working
$this->hasOne(Bid:class, 'auction_id')->ofMany('price','max')->approved(); // not working
$this->hasOne(Bid:class, 'auction_id')->approved()->ofMany('price','max'); // not working
Unfortunately these shouldn't be a relationships
Real question is why are you trying to make these relationships?
Usually you should be using relationships on model to describe how they are correlating together within the database, the rest of the things you should be defining as a scope on a query or a model, or as an attribute.
So, what I'm trying to say is this:
Keep bids as a relationship, as that is actually a relationship to the Bid model
Update approvedBids to be a scope (or an attribute)
Update topBids to be a scope (or an attribute)
Then, you will be able to find top bid easily by doing something like this:
$this->topBids->first() -> if it is an attribute
$this->topBids()->first() -> if it is a scope
This is how you can create a scope: https://laravel.com/docs/9.x/eloquent#local-scopes
In the end, you can even create an attribute that will allow you to retrieve topBid like this:
public function getTopBidAttribute(){
$this->bids()->approved()->orderByDesc('offered_token_price')->first();
}
Then later you can just do $this->topBid.
I think I've found the solution
public function topBid(): HasOne
{
return $this->hasOne(Bid::class, 'auction_id')
->approved()
->orderByDesc('price');
}
You see the problem was in ofMany() function, which creates a huge SQL and I don't know why!
I've returned a HasOne object here, which supports all kinds of query manipulations. Basically HasOne class, tells the main query, to:
Retrieve the first record of the query I've provided.
So if we use orderBy it only provides an order for HasOne's query. and the main query will take cares of the rest and selects the first record.
I have 3 data models, one of which extends the other:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Opinion extends Model
{
public function reactions()
{
return $this->morphMany('App\Models\Reaction', 'reactable');
}
...
}
namespace App\Models\Activity;
use App\Models\Opinion;
class ActivityOpinion extends Opinion
{
...
}
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Reaction extends Model
{
public function reactable()
{
return $this->morphTo();
}
...
}
The App\Models\Opinion model has a polymorphic relationship with the App\Models\Reaction model. I can retrieve all of the App\Models\Opinion reactions no problem, so I know the relationship works great.
My question is, how can I retrieve the same set of reactions from the App\Models\Activity\ActivityOpinion model? Because right now, it is looking for App\Models\Activity\ActivityOpinion as the relationship but I need it to look for App\Models\Opinion. Is it possible to mock another model in a polymorphic relationship?
This is because in a Polymorphic Relationship in the stored data (if leaved as default) the relationship type gets the class namespace (sort of) to specify wich model needs to be returned. That's why when you try to access to your reactions() relationship from ActivityOpinion it will look up for the App\ActivityOpinion value in the reactable_type.
You can customize the morph class to search in the model addind this:
Opinion.php
protected $morphClass = 'reaction';
This should be enough, if not, add it also in the ActivityOpinion model.
Note
This could breake some things when trying to search results using Eloquent. Check this other answer in order to address this possible inconviniance.
Update
I've just found out that you could do all this even easier with MorphMap. From the docs:
Custom Polymorphic Types
By default, Laravel will use the fully qualified class name to store
the type of the related model. For instance, given the one-to-many
example above where a Comment may belong to a Post or a Video,
the default commentable_type would be either App\Post or
App\Video, respectively. However, you may wish to decouple your
database from your application's internal structure. In that case, you
may define a "morph map" to instruct Eloquent to use a custom name for
each model instead of the class name:
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'posts' => 'App\Post',
'videos' => 'App\Video',
]);
You may register the morphMap in the boot function of your
AppServiceProvider or create a separate service provider if you
wish.
I'm sure this is a totally simple question but for the life of me I'm stuck here- we're using Eloquent outside of Laravel due to PHP restrictions. I have a support ticket tracking app that I'm trying to update.
The data structure of this app is such that each ticket is assigned a UUID on submission and a table with that UUID as its name is generated and all changes to the ticket are tracked as new entries in that table.
Following some tutorials on Eloquent I got our models and controllers set up and working but for each one I see that I'm defining the table name in the model itself. IE our ticket model is
namespace Models;
use \Illuminate\Database\Eloquent\Model;
class Ticket extends Model {
protected $table = 'tickets';
protected $fillable = [table columns here];
}
and anything called in the tickets controller correctly and successfully reads and writes data to our tickets table.
So... my question is: how would I go about reading/writing/creating/deleting those previously mentioned UUID tables?
I've tried the built in table selector (ie- DB::table(uuid here) and DB::setTable(uuid here) but to no avail. I get Fatal error: Call to undefined method Models\Database::setTable()
What I'm after is a model/controller that I can reuse for ANY dynamically-named table.
You could create a generic model and dynamically set the table name, like this:
namespace Models;
use \Illuminate\Database\Eloquent\Model;
class FormerUUIDTicket extends Model {
protected $table = 'some_table';
protected $fillable = [table columns here];
}
class SomeController
{
public function someAction()
{
$uuid = $_POST['uuid_field']; //some uuid, the table name
$model = new FormerUUIDTicket;
$model->setTable($uuid);
return $model->get(); //do anything using eloquent with proper table
}
}
Make sure that you always set the table name before use, or it will fail. Don't use static function either, for the same reason.
I have recently started working with Laravel and am looking to really start digging into it and all of its power. I am currently trying to do something fairly simple, pair a player with their corresponding stats. I have 3 classes and corresponding tables in my database:
Player
Stats
PlayerStatsLink
The PlayerStatsLink table has a 'player_id' and a 'stats_id' column that connects each player to their corresponding stats. Where I am getting somewhat confused is how to use the belongsTo and hasOne methods in this situation with the link. Right now here is what my classes look like:
Player.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Player extends Model
{
public function stats()
{
return $this->hasOne('App\Stats');
}
}
Stats.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Stats extends Model
{
public function players()
{
return $this->belongsTo('App\Player');
}
}
I am confused as to how to incorporate my link class into the mix, even though I'm pretty sure it is the right thing to do based on what I have learned about databases and structures. I have considered adding a player_id column to the Stats table to make what I have right now work, but I'm not sure if that is the right way to do this.
What I would ideally like to be able to do to access this data after the PlayerStatsLink has connected a player with corresponding stats is something like this:
{{ $player->stats->points }}
Okay let us assume you have 3 models: User, Stat and GameLog. I suggest you to use singular name for your models to avoid any confusion.
let us start with creating them:
php artisan make:model --migration Stat
to create both Stat model and stats table.
php artisan make:model --migration GameLog
to create both GameLog model and game_logs table.
This is the easiest way to create models and tables and bind them together and avoid typos. User model and corresponding table is present with fresh laravel installation.
in you User model:
public function gameLogs(){
return $this->hasMany(GameLog::class);
}
public function stats(){
return $this->hasManyThrough(Stat::class, GameLog::class);
}
in GameLog model:
public function user(){
return $this->belongsTo(User::class); // each gamelog belongs to just one user
}
public function stat(){
return $this->hasOne(Stat::class);
}
then in your Stat model:
public function gameLog(){
return $this->belongsTo(GameLog::class);
}
Please remember to construct you database also. If you faced problem in it please let me know and I will help you with.
Now if you want to query a User gamelogs you simply need to:
$user = User::find(1);
$gamelogs = $user->gameLogs;
since $user has many gamelogs you need to iterate trough them:
foreach(gamelogs as gamelog){
//do your logic
}
also if you want to load stat relationship of gamelog and use it then please read section eager-loading in laravel documents and learn about that.
I hope the explanation is clear and enough. If not please let me know. But I have a suggestion for a better database structure. I think it is better to merge stats table and game_logs table together and omit the model GameLog or Stat. Practically it is exactly the same.
Get the feeling I'm being particularly dumb here but it's doing my nut in.
I have a candidate table with the country_id field as a foreign key to the countries table. A candidate has one linked country.
The Candidate model has a relationship defined as:
public function country()
{
return $this->belongsTo('App\Models\Country', 'country_id');
}
And the Country model has the following:
public function candidate()
{
return $this->hasMany('App\Models\Candidate');
}
On my listing page I'm using Candidate::with('country')->paginate(5) to provide a list of the Candidates on screen. When I come to display the country I use:
$candidate->country->country
But this returns the error:
Trying to get property of non-object
I can, however, use
$candidate->country['country']
Using var_dump on $candidate->country suggests that an object is returned, so why can't I access it as an object?
EDIT:
The candidates controller is namespaced to admin as follows:
namespace App\Controllers\Admin;
and as part of the declarations in the controller I have:
use App\Models\Candidate, App\Models\Manufacturer, App\Models\Make, App\Models\Country;
Each model is namespaced with
namespace App\Models
Have I even got the relationship set up correctly?
Thanks