I am using Laravel 5.4 and having trouble figuring out why my morphTo relationship always returns null no matter what I do. The inverse of the relationship is fine, but when I try to retrieve the owner of the polymorphic relation, it is null.
class Opportunity extends Model {
public function Organization() {
return $this->morphTo('Organization');
}
}
class Account extends model {
public function Opportunities() {
return $this->morphMany('App\Opportunity', 'Organization');
}
}
class Department extends model {
public function Opportunities() {
return $this->morphMany('App\Opportunity', 'Organization');
}
}
$org = App\Opportunity::findOrFail(1)->Organization;
The full namespace is stored in the database and the _type and _id actually have the appropriate organization type and id in the columns (i.e., 'App\Account' and '456'). So, I know the database record and the returned Opportunity object have the correct Organization in the columns (I can see it in the database correctly), but no matter what I do, if I try to retrieve Organization it is null.
Here is the output. You will notice the Organization is in the attributes, but the relation is null and I cannot get it to return even adding ->with('Organization') to the query. It doesn't even seem to be executing the query to get the owner
#primaryKey: "ID"
+timestamps: true
#guarded: []
#hidden: []
#visible: []
#with: []
#dissociateRelations: []
#connection: null
#keyType: "int"
+incrementing: true
#perPage: 15
+exists: true
+wasRecentlyCreated: true
#attributes: array:13 [
"StageID" => 12
"TypeID" => 1
"OriginID" => 20
"Description" => "Interested in scanner fi6140"
"UserID" => 3
"SolutionValue" => ".00"
"MarginValue" => ".00"
"created_at" => "2010-09-16 11:19:00.000"
"updated_at" => "2015-09-01 12:32:00.000"
"_migrationID" => "4299"
"Organization_type" => "App\Account"
"Organization_id" => 456
"ID" => 1
]
#original: array:13 [
"StageID" => 12
"TypeID" => 1
"OriginID" => 20
"Description" => "Interested in scanner fi6140"
"UserID" => 3
"SolutionValue" => ".00"
"MarginValue" => ".00"
"created_at" => "2010-09-16 11:19:00.000"
"updated_at" => "2015-09-01 12:32:00.000"
"_migrationID" => "4299"
"Organization_type" => "App\Account"
"Organization_id" => 456
"ID" => 1
]
#dateFormat: null
#events: []
#observables: []
#relations: array:3 [
"Organization" => null
"Projects" => Illuminate\Database\Eloquent\Collection {#3463
#items: []
}
"Tickets" => Illuminate\Database\Eloquent\Collection {#3443
#items: []
}
]
#touches: []
#forceDeleting: false
change your morphto to this base on document to prevent confusing laravel to detect column type and column id
class Image extends Model
{
protected $fillable = [
"link",
"imageable_id",
"imageable_type",
];
public function imagable()
{
return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id');
}
}
So, it looks like I may have discovered my problem, but I do not know why. When the owner is queried by
App\Opportunity::findOrFail(1)->Organization
it looks like Eloquent is looking for organization_type and organization_id (with lowercase) and not Organization_type and _id. However, my migration uses $table->morphs('Organization') and so the columns in the database are created with the uppercase. When I change that to lowercase in the database, my results get returned. Not sure how to change that behavior though, and it seems to have been introduced after upgrading from 5.2
Edit: there was a change introduced in 5.3 that snake cases the _type and _id that seems to be the root cause of my experience
https://github.com/laravel/framework/pull/15334
After losing two half-days exploring all the solutions found on the net, I emptied the cache and everything works ...
php artisan cache:clear
Related
I have one to many relationship between users and posts.
I want to know if I eager load in the posts can I modify the collection without a new attribute getting created on the User model.
$user = User::with('posts')->get();
// Filter the posts on the user using business logic.
// Below is an example. I DO NOT want to do this logic in the db/query builder.
// Arbitrary business rule that is not easily possible to calculate in DB
$shouldGetTestPost = true;
$user->posts = $user->posts->filter(function($post) use ($shouldGetTestPost) {
if ($shouldGetTestPost && $post->name = 'test') {
return true;
}
return false;
});
dd($user);
If I run the above code laravel will create a new attribute called posts and assign the collection to that attribute instead of modifying the relationship.
e.g.
// I've removed irrelevant model data
App\User {#1145
#table: "users"
#attributes: array:6 [
"id" => 1
"email" => "test#test.com"
"password" => "secret"
"updated_at" => "2019-02-11 18:56:35"
"created_at" => "2019-02-11 18:56:35"
// Below is new
"posts" => Illuminate\Database\Eloquent\Collection {#1217
#items: array:1 [
0 => App\Post {#1269
#table: "posts"
#attributes: array:24 [
"id" => 1
"name" => 'test'
"user_id" => 1
]
}
]
}
]
#original: array:5 [...]
#relations: array:1 [
"posts" => Illuminate\Database\Eloquent\Collection {#1264
#items: array:2 [
0 => App\Post {#1269}
1 => App\Post {#1234
#table: "posts"
#attributes: array:24 [
"id" => 1
"name" => 'test'
"user_id" => 1
]
}
]
}
]
]
Another interesting thing that happens is that in #relations the data is removed from theitems array but the reference still remains. See 0 => App\Post {#1269}
Is this intended for laravel? I'm aware that laravel prioritises attributes over relations but it seems odd that it wouldn't change the relations array.
How would I only change the relations collection and not create a new attribute?
The setRelation method can be used to override what's stored in a relation.
$model->setRelation($relation, $value)
https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Concerns/HasRelationships.html#method_setRelation
However, there's very few situations where this would be useful so I still believe this is an XY problem. You can constrain the eager loading query by passing a closure as the second argument to with() which would be the solution to conditionally retrieving related items.
I am using pagination in Laravel 5.6 to build an application. I am using Laravel DB instead of Eloquent. However, when I add a pagination link on view, it shows an error Call to a member function links() on array. This is because I am using an array, the default is an object with a collection. The output I am getting looks like
array:12 [▼
"current_page" => 1
"data" => array:3 [▶]
"first_page_url" => "http://127.0.0.1:8000/posts?page=1"
"from" => 1
"last_page" => 3
"last_page_url" => "http://127.0.0.1:8000/posts?page=3"
"next_page_url" => "http://127.0.0.1:8000/posts?page=2"
"path" => "http://127.0.0.1:8000/posts"
"per_page" => 3
"prev_page_url" => null
"to" => 3
"total" => 9
]
When I use normal pagination i.e. without converting to an array, the output is
LengthAwarePaginator {#264 ▼
#total: 9
#lastPage: 3
#items: Collection {#251 ▶}
#perPage: 3
#currentPage: 1
#path: "http://127.0.0.1:8000/posts"
#query: []
#fragment: null
#pageName: "page"
}
The problem I face here is adding other data to collection
I can't use the default output return by pagination() method. Can someone please suggest how to solve this issue?
Using the below, I am getting the required output.
Step 1: Include LengthAwarePaginator in the file
use Illuminate\Pagination\LengthAwarePaginator;
Step 2: Create an object of LengthAwarePaginator
$paginate = new LengthAwarePaginator(Array, countOfArray, perPage, currentPage, [
'path' => request()->url(),
'query' => request()->query()
]);
I kept currentPage value as null.
Return $paginate to view.
return view('home', ['data' => Array, 'page' => $paginate]);
Maybe is silly question but I have this in my Controller which is showing the form for image upload and after form is submitted should return me to another view.
On this another view I have passing variable with all images but still I've got
Undefined variable: images
So this is what I have in my Controller
// Display all Images
public function images()
{
$images = Images::paginate(3);
return view('images', compact('images'));
}
// Display image upload form
public function imageCreate()
{
return view('create');
}
// Submit and store image
public function imageStore( Request $request )
{
$image = new Images([
'caption' => $request['caption'],
'name' => $request['caption'],
'path' => 'uploads/noimage.png',
'hits' => 1,
'added_on' => '2017-08-08 9:00:00'
]);
$image->save();
return view('images', compact('images'));
}
And this in my view images.blade.php
#foreach($images as $image)
<img class="thumbnail block" src="{{ '../'.$image->path }}">
#endforeach
So, why variable is undefined if I posting it in the return view statement?
Update: dd($image) in the view return
Images {#234 ▼
#primaryKey: "id"
#table: "images"
+timestamps: false
+fillable: array:6 [▶]
#connection: null
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: true
#attributes: array:7 [▼
"caption" => "dasdsadsad"
"name" => "dasdsadsad"
"path" => "uploads/noimage.png"
"hits" => 1
"added_on" => "2017-08-08 9:00:00"
"slug" => "dasdsadsad-7"
"id" => 144
]
#original: array:7 [▶]
#casts: []
#dates: []
#dateFormat: null
#appends: []
#events: []
#observables: []
#relations: []
#touches: []
#hidden: []
#visible: []
#guarded: array:1 [▶]
}
Update 2: routes
Route::get('images', 'HomeController#images');
Route::get('create',['as'=>'create','uses'=>'HomeController#imageCreate']);
Route::post('create',['as'=>'store','uses'=>'HomeController#imageStore']);
The issue is here:
compact('images'));
but your variable is $image. So change it to:
compact('image'));
and try again. And also change the foreach() variable name like:
#foreach($image as $img)
...
$img->slug
$img->path
Explanation:
The variable that contains the data is $image and the one you are passing from controller is compact('images')). An extra s is there.
passing-data-from-controller-to-views
error int this line
return view('images', compact('images'));
your variable name is $image and you pass $images
replace above line with this
return view('images', compact('image'));
public function imageStore( Request $request )
{
$image = new Images([
'caption' => $request['caption'],
'name' => $request['caption'],
'path' => 'uploads/noimage.png',
'hits' => 1,
'added_on' => '2017-08-08 9:00:00'
]);
$imagePath='uploads/noimage.png';
$image->save();
return view('images')->with('image',$imagePath);
}
you get path like this
<a href="{{ URL::to('image/'.$image->slug) }}">
<img class="thumbnail block" src="{{url('/'.$image)}}">
</a>
Laravel assumes when you use the compact function that the variable you are passing is named the same as the variable you are passing in the route. For instance:
route::get('/customer/{customer_id}', 'CustomerController#show');
Therefore in your controller when returning data with your view you need to do something like:
return view('customer', ['customer_name' => $customer->name]);
Then you should be able to reference it in your view like:
{{$customer_name}}
Vender hasMany Banquet
Here is my query:
$banquet = Vendor::with(['banquet' => function($query){
$query->where('active',1)->where('id' ,1)->first();
}])->findOrfail(1);
but the relation part return an collection like get() method not first()
#relations: array:1 [▼
"banquet" => Collection {#283 ▼
#items: array:1 [▼
0 => Banquet {#285 ▼
How to trans like this? So that I can just get the banquet data like $vendor->banquet not $vendor->banquet[0]
#relations: array:1 [▼
"banquet" => Banquet {#285 ▼
I think, you are using OneToMany for banquet.
So, you are getting in the collection/array.
Please, try it as OneToOne. and say if you get any change or not.
return $this->belongsTo();
in banquet method.
or
return $this->hasOne();
Let's say that we have Laravel's default User.php model, and we also have UsersController.php. Here's how the create method looks like:
public function create(User $user)
{
return view('backend.users.form', compact('user'));
}
As you can see - User $user is passed as argument, and view backend.users.form receives that $user.
If I add dd($user); in the create method:
public function create(User $user)
{
dd($user);
return view('backend.users.form', compact('user'));
}
the result will be:
User {#193 ▼
#fillable: array:3 [▼
0 => "name"
1 => "email"
2 => "password"
]
#hidden: array:2 [▼
0 => "password"
1 => "remember_token"
]
#connection: null
#table: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: []
#original: []
#relations: []
#visible: []
#appends: []
#guarded: array:1 [▶]
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: false
+wasRecentlyCreated: false
}
So, variable (instance) $user exists, right?
But, in a view backend.users.form that receives $user, if we do something like this:
<h3>{!! $user->exists ? 'Editing '.$user->name : 'Create New User' !!}</h3>
The result will be as if $user does not exist? The result will be Create New User. I do not understand it.
Can you explain to me why $user->exists returns false when dd($user); shows that it exists?
Without knowing your DI-container configuration, the User instance you inject probably does not exist as a record in the database - it has no identity. It is merely a model instance you can use.
Can you explain to me why $user->exists returns false when dd($user);
shows that it exists?
Please look carefully at the result of dd() inside a controller:
...
+exists: false
...
$user->exists is false here, so it will return false in any case.