Property [business_url] does not exist on this collection instance - php

My business can have many pages and here i am defining my model:
class PageList extends Model
{
public function business()
{
return $this->hasMany('App\Business','business_id');
}
}
My Controller
public function pageListHere()
{
$list = PageList::all();
return view('page-list',compact('list'));
}
and my view having for each loop
<td>{{optional($value->business)->business_url}}.spikesales.io/{{$value->page_url}}</td>
In my relation ship if i do hasOne('App\Business','business_id'); it works fine but its only display one page against business but i have multiple pages in my database and one business can have many pages and if write hasMany('App\Business','business_id'); it show me the error Property [business_url] does not exist on this collection instance
I want to show the multiple pages against one business.
your help will be highly appreciated!

I think you are a little bit confused. In your model, you are actually defining the relation A page has many businesses, and you are telling that the businesses table has a column business_id, which Laravel tried to find but couldn't, resulting in an error.
The hasMany function works in this way:
// In the Post Model...
public function comments() {
return $this->hasMany('App\Comment', 'post_number');
}
This makes the relation A Post has many Comments and makes Laravel assume that the comments table has post_number column.
You want to show multiple pages against one business. So, A business has many Pages. To code it...
// In your BUSINESS model...
public function pages() {
// Notice that $this refers to the Business Model
return $this->hasMany('App\PageList', 'business_id');
}
You can optionally (but I think you should) omit the 'business_id' part. It will safely be assumed by default.
And the inverse of the relation, A Page belongs to A business, will be coded as...
// In your PAGELIST model...
public function business() {
return $this->belongsTo('App\Business');
}

You need to loop/iterate over the business relationship since the result will return in a array of collection:
#foreach($lists as $list)
// others markup
#foreach($list->business as $business)
// do something here
{{ $business->business_url }}
#endforeach
#endforeach

Related

Laravel 8.x realationship - Use HasMany in HasOne

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.

Laravel relationship with a link table - Confused

I'm playing with laravel for the first time and i'm a little confused on how to achieve a link table relationship. I have 3 tables: Tank, TankContent and Content
I have the following models:
Tank:
class Tank extends Model
{
public function getTankContent()
{
return $this->hasMany('App\TankContent', 'tankID', 'tankID');
}
}
TankContent:
class TankContent extends Model
{
public function getTank()
{
return $this->belongsTo('App\Tank', 'tankID', 'tankID');
}
public function getContent()
{
return $this->belongsTo('App\Content', 'contentID', 'contentID');
}
}
Content:
class Content extends Model
{
public function getContentTanks()
{
return $this->hasMany('App\ContentTank', 'contentID', 'contentID');
}
}
Now im trying to call say tankID 2 and get all the content details inside of that
$content = Tank::find(2);
$items = $content->getTankContent;
This will list me the content. But then how do i go about linking the results to getContent() from the TankContent model?
Thanks in advance. Hopefully I just need it explaining and then it will all click.
p.s i have tried reading the https://laravel.com/docs/5.5/eloquent-relationships#many-to-many and im still stumped!
TankContent isn't required, the belongsToMany relationship methods belong on the Tank and Content models.
https://laravel.com/docs/5.5/eloquent-relationships#many-to-many
From here you can see there is no RoleUser model, the models are only Role and User. The belongsToMany relationship on those models defines that the intermediate, or pivot, table is role_user.
You can additionally define other fields on that relationship with withPivot() chained onto the belongsToMany method. (read "Retrieving Intermediate Table Columns"). Therefore, there is likely no reason to have a separate model for the intermediate/pivot table.

Using Laravel Eloquents HasManyThrough relation with multiple relations through polymorphism

I got a rather simple application where a user can report other users comments and recipes. I use a polymorphic relation to store the reports. Which works fine; however, I am now trying to get the offences that a user has made.
Getting users reports is not a problem, this can be done directly using user->reports() but I would very much like to get the reports in which other people has reported said user. I can get this to work using either the hasManyThrough relation or queries BUT only on one model at a time.
ex.
public function offenses() {
return $this->hasManyThrough('Recipe', 'Reports');
}
Or
->with('user.recipe.reports')
The problem is that my reportable object is not just recipes, it could be comments, images, etc. So instead of having to use multiple functions, the logical way would be to parse the relationship between hasManyThrough various parameters somehow.
Theoretically looking like this:
public function offenses() {
return $this->hasManyThrough(['Recipe', 'RecipeComments'], 'Reports');
}
Is this in any way possible? With some undocumented syntax? If not is there any clever workarounds/hacks?
Possible solution?
Would an acceptable solution be to add another column on my report table and only add offender_id like this?
ID | User_id | Offender_id | Reportable_type | Reportable_id
That would mean I could just make a relation on my user model that connects offences through that column. But would this be considered redundant? Since I already have the offender through the reportable model?
Models
Polymorphic Model
class Report extends Model {
public function reportable() {
return $this->morphTo();
}
public function User() {
return $this->belongsTo('App\User');
}
}
Recipe Model
class Recipe extends Model {
public function user() {
return $this->belongsTo('App\User');
}
public function reports() {
return $this->morphMany('App\Report', 'reportable');
}
}
Comment Model
class RecipeComment extends Model {
public function user() {
return $this->belongsTo('App\User');
}
public function reports() {
return $this->morphMany('App\Report', 'reportable');
}
}
Using your current model, you can receive all the reported models for a user by the following code:
$recipeCommentReport = RecipeComment::whereHas('reports',function($q){
return $q->where('user_id','=',Auth::user()->id)
});
$recipeReport = Recipe::whereHas('reports',function($q){
return $q->where('user_id','=',Auth::user()->id)
});
//Get all reports into one
$reports = $recipeReport->merge([$recipeCommentReport]);
This is messy at best, because:
We can't sort the results, given that we are using two separate db queries.
If you have other models who have a report relationship, just imagine the chaos.
The best solution, is as you have figured out above:
Add an offender_id column to your report table.
It is cleaner and follows the DRY principle.
Typical Case Scenarios
Get all Recipe Comment reports for User
Report::where('offender_id','=',Auth::check()->id)
->where('reportable_type','=','RecipeComment')
->get();
Count offense by type for User
Report::where('offender_id','=',Auth::check()->id)
->grouBy('reportable_type')
->select(Db::raw('count(*)'),'reportable_type')
->get();

Laravel Eloquent can't get simple hasMany relation working

Background
I have an internationalized DB that stores its strings for different languages like this:
products
id
price
product_translation
id
product_id
language_id
name
description
languages
id
name (e.g. 'English', 'German')
code (e.g. 'en', 'de')
With appropriate Models for each table (Product, ProductTranslation, Language). In my views I want to fetch a list of products like this:
// get first 20 products, list name and price.
#foreach(Product::take(20)->get() as $product)
{{$product->translations->name}} {{$product->price}}
#endforeach
Problem
My app will return product names according to what the current App::getLocale() is set to (i.e. en and de).
I'm just starting out with Laravel's Eloquent, I'm unsure how to specify the correct relationships (or if I'm actually doing it correctly at all).
My attempt
I have specified a OneToMany relationship in between Product and ProductTranslation:
class Product extends \Eloquent {
protected $with = ['translations'];
public function translations()
{
return $this->hasMany('ProductTranslation');
}
}
This works fine but will return all the translations (we only want the current locale).
I then specify a OneToOne relationship between ProductTranslation and Language:
class ProductTranslation extends \Eloquent {
protected $with = ['language'];
public function language()
{
return $this->hasOne('Language')
->where('code', App::getLocale());
}
}
I know this doesn't work and I am stumped at what to do next. Does anyone have a cleaner approach?
class ProductTranslation extends \Eloquent {
protected $with = ['language'];
public function language()
{
return $this->hasOne('Language');
}
}
In route or controller
ProductTranslation::language()->where('code', '=', App::getLocale())->get();
To keep this in the model do this
public static function getLocale()
{
return static::language()->where('code', '=', App::getLocale())->get();;
}
Call the function using ProductTranslation::getLocale()
Laravel has built-in system for translations and with a little work you can make it work with the database, however that is probably not what it was designed for.
You can't fetch the ones you want cause relationships are based on ids (foreign keys) and not for string constraints or similar.
In your view you could look into filtering out the ones that are not for the language code you wanted using filter(): http://laravel.com/docs/4.2/eloquent#collections
Or You could consider moving the translations to the proper place as hard-coded: http://laravel.com/docs/4.2/localization and if that is not possible you could look into fetching the translations from database still using the method described by that link. Eventually it just returns the array of the translations and does not care how did you build the array, hard-coded or from database.

Laravel - Selecting specific columns on many-to-many relationships

I have two models in my Laravel 4.2 web application, User and Group. A user can be a member of many groups, and a group can have many members. Both models are thus joined with a many-to-many relationship:
<?php
class User extends Eloquent {
public function groups()
{
return $this->belongsToMany('Group');
}
}
class Group extends Eloquent {
public function users()
{
return $this->belongsToMany('User');
}
}
?>
One of my API resources is /groups, which lists all groups available within the app:
<?php
$groups = Group::with('users')->all();
?>
This works, however in the JSON response each user contains all fields from the users table (excluding of course those in the $hidden attribute). I would like this relationship to return only a specific set of fields instead of the whole table.
In other relationship types I can easily achieve this with the following statement (assume now that users may belong to only one group):
<?php
public function users()
{
return $this->hasMany('User')->select(['id', 'first_name', 'last_name']);
}
?>
However the above does not seem to work with many-to-many relationships. I came across this question which apparently refers to the same issue and it looks like this was not possible in Laravel 4.1. The author of the chosen answer, tptcat, provides a link to an issue on Laravel's Github issue tracker, but the link is no longer working and I couldn't figure whether this issue is still open in 4.2.
Has anybody come across this and successfully managed to solve it?
{
return $this->belongsToMany('User')->select(array('id', 'name'));
}
use this
The all method takes in an array of column names as a parameter.
If you look at the source, it takes * (which means everything) by default.
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L624-L629
You can pass in the columns that you needed and it should return the results only with the specified columns.
<?php
$groups = Group::with('users')->all(array('first_column', 'third_column'));
Use like this.
<?php
class User extends Eloquent {
public function groups()
{
return $this->belongsToMany('Group')->select(array('id', 'name'));
}
}
class Group extends Eloquent {
public function users()
{
return $this->belongsToMany('User')->select(array('id', 'name'));
}
}
?>
Instead of selecting column in relationship, you can select column as below:
$groups = Group::with('users:id,first_name,last_name')->all();
And when you are selecting column in relationship, make sure that you are selected foreign key of relation table

Categories