I have been using Laravel for months, and the inverse ralation bothers me.
Considering the following example.
There are two Eloquent models, and the relation is defined as below
class Customer extends \Illuminate\Database\Eloquent\Model
{
public function orders()
{
return $this->hasMany(Order:class);
}
}
class Order extends \Illuminate\Database\Eloquent\Model
{
public function customer()
{
return $this->belongsTo(Customer:class);
}
}
$customer1 = Customer::select()
//->where('id', '=', 1)
->with('orders')
->first();
$customer2 = $customer1->orders[0]->customer;
if ($customer1 === $customer2) {
echo 'Same'. PHP_EOL;
} else {
echo 'Not the same'. PHP_EOL;
}
$customer1 and $customer2 should be the same. But they are not.
What I want is that $customer1 and $customer2 be the same, just like Yii2 does
With the word same, I mean they should be the same instance instead of two instances with the same customer data.
Did I miss something? Any help would be grateful.
Laravel has no active record implementation. There is no master who keeps track of what objects have been fetched from the database. Each object tracks its own changes, but has no knowledge of other objects that exist in memory.
From your relation, you can see every customer can have multiple order and order must have a customer. So in your $customer1 variable, you have an eloquent model object and in $customer2 you have an id. How could you compare them?? Try to compare between ID. Laravel doesn't work that way you are expecting.
$customer1 = Customer::select()
//->where('id', '=', 1)
->with('orders')
->first();
$customer2 = $customer->orders[0]->customer;
if ($customer1->id === $customer2) {
echo 'Same'. PHP_EOL;
} else {
echo 'Not the same'. PHP_EOL;
}
Related
One bit of "magic" in the Laravel codebase is here in HasOneOrMany.php where if the underlying query behind a hasOne relationship technically fetches multiple rows, it will just return the first row instead.
protected function getRelationValue(array $dictionary, $key, $type)
{
$value = $dictionary[$key];
return $type === 'one' ? reset($value) : $this->related->newCollection($value);
}
This got me thinking that, given a related model that is normally hasMany, an oldestOfMany() relation can be simulated with just an orderBy, e.g.
public function models()
{
return $this->hasMany(Model::class);
}
public function firstModel()
{
return $this->hasOne(Model::class)->orderBy('created_at');
}
Are there any downsides to this shortcut approach?
As pointed out in the comments, calling ->first() on the many relationship works just fine for actually retrieving a model. In our app it's common to build relations from other relations, and I want to do that here. e.g.
public function firstActiveModel()
{
return $this->firstModel()->active();
}
I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)
On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();
I have two tables, organizations and categories. This is how my tables and models are set up:
Tables:
organizations
id - integer
category_id - integer, fk
name - string
...
categories
id - integer
name - string
Models:
class Organization extends Eloquent
{
public function category()
{
return $this->belongsTo('Category');
}
public function comments()
{
return $this->morphMany('Comment', 'commentable');
}
}
class Category extends Eloquent
{
public $timestamps = false;
public function organization()
{
return $this->hasMany('Organization');
}
}
In my routes.php file, I have the following code (where $category is equal to "organizations"):
$query = Organization::query()
->join('categories', 'categories.id', '=', $category . '.id')
->select('categories.name as category');
$tiles = $query->get();
In my view, I am able to perform the following actions without any errors:
foreach($tile->comments as $comment)
{
...
}
...
$tile->name;
However, calling $tile->category->name will give me the error Trying to get property of non-object. And calling $tile->category will just return null. How can I display the category associated with the organization? I am able to retrieve other properties and relations just fine, but this is giving me a problem.
You have a bug in your code: $category . '.id' should be $category . '.category_id' doing so should make $tile->category->name work.
Also please note (you probably already know this) that in the code that you provided you are not actually using the belongsTo relation as set in your Organization class, you are just using the joined data.
The following would also work using the Eloquent ORM utilizing the models relationship methods:
$tiles = Organization::with('category')
->get();
foreach ($tiles as $tile)
{
echo $tile->name . '<br>';
echo $tile->category->name . '<br>';
}
Or you could do so the other way round from the categories model like so:
$tiles = Category::with('organization')
->get();
foreach ($tiles as $tile)
{
echo $tile->name . '<br>';
foreach($tile->organization as $org)
{
echo $org->name . '<br>';
}
}
I am making a social network using Codeigniter and I'm trying to make the users view show a button when you are already following a person how do I do that??
Model
<?php
class User_model extends CI_Model {
function get_All(){
$this->db->select('*');
$this->db->from('membership');
// $this->db->join('following', 'following.id = membership.id');
$q = $this->db->get();
if($q->num_rows() > 0) {
foreach ($q->result() as $rows) {
$data[] = $rows;
}
return $data;
}
}
It's a little bit of work. Assuming that all users can be searched, you might have a list of all users, and of the 'friends' of all users. Then you run through the two comparing them. The following is an idea:
Database Members: All members |ID_User|...other user stuff...
Database Followers: Lists all friendship |ID_User|ID_Follower|
You need two queries for this: One for all, and one for Followers where ID_User=$UserID. As you run through all members as you suggest, then you can
function get_Followers(){
$friends= $this->db->query('SELECT * FROM Followers WHERE ID_User=$UserID');
$follow_array=$row->ID_Follower;
}
Now:
$follow_array //A list of all friends.
$q //Your original list of members
Then you can use:
if($q->num_rows() > 0) {
foreach ($q->result() as $rows) {
if(in_array($rows->ID_Follower, $Follower_array))
{
$data=true;
}
else
{
$data=false;
}
}
return $data;
}
Beware however, this may be a very server-heavy operation.
I'm going to assume you're talking about visiting a persons profile and if you are following that person you see a button. That only requires one query and logic in the view itself.
Going on this assumption you're already passing the person who's profile you're viewing to the view so all you need is a list of people the viewer is following to compare that id to.
I'm not really sure what your query in your code is doing since there's no description of your database so I'll give the code the way the tables should be laid out, which is a user table and a following table that is just a join table on the users. Assuming the following table has 3 columns id, userId, followingId where userId is you and followingId is the person you are following.
Controller
$data['following'] = $this->modelName->getFollowing($userId); //pass current userId
$this->load->view('viewName',$data);
Model:
public function getFollowing($userId)//$userId to be passed as the person viewing.
{
$this->db->where('userId',$userId);
$data = $this->db->get('following');
if($this->db->num_rows() > 0)
{
return $data;
} else {
$data=array();
$return $data;
}
Then in the view it's a simple if statement.
if(in_array($profileId,$following))
{
echo button;
}