Student.php
It has many fees
class student extends Model{
public function fees($round){
return $this->hasMany(fee::class)->where('payment_round','=',$round);
}
}
Fee.blade.php
#foreach($student->fees("parameter") as $fee)
{{$fee->myproperty}}
#endforeach
How I can pass a parameter to $student->fees("parameter")?
You can keep relationship as simple as possible and add a second method which uses that to get the data you want :
<?php
class student extends Model{
public function fees(){
return $this->hasMany(fee::class);
}
public function getFeeByRound($round)
{
return $this->fees()->where('payment_round','=',$round)->get();
}
}
And then you can use this as :
#foreach($student->getFeeByRound($parameter) as $fee)
{{ $fee->myproperty }}
#endforeach
Try something like that:
#foreach($student->fees($parameter)->get() as $fee)
{{ $fee->myproperty }}
#endforeach
I think you had confused the collection of related models fees with the query constructor fees().
Related
i have a problem with laravel model relationship. The codes are as follows:
Route:
Route::get('/customers','Back\CustomerController#index')->name('admin.customer');
Models\Customer.php:
class Customer extends Model{
public function getData(){
return $this->hasMany('App\Models\Datapanel','name','name');
}
}
Models\Datapanel.php:
class Datapanel extends Model{
//
}
CustomerController.php:
class CustomerController extends Controller{
public function index(){
$customers=Customer::orderBy('created_at','desc')->get();
return view('back.customer',compact('customers'));
}
}
customer.blade.php:
#foreach ($customers as $customer)
<div class="tab-pane" id="{{$customer->name}}" role="tabpanel">
{{$customer->getData}} //this line working and calling all data of the same name, but in array format.
{{$customer->getData->comment}} //this line is what I want, but it doesn't work and gives the error Property [comment] does not exist on this collection instance
{{$customer->getData->first()->comment}} //this line working, but calling first data and i want all the data of the same name.
{{$customer->getData->all()->comment}} //I wish there was something like but it donesn't work.
</div>
#endforeach
Since getData is returning a HasMany relationship it will be a collection and not a single record. So need to loop over the collection to display data. See below
#foreach ($customers as $customer)
<div class="tab-pane" id="{{$customer->name}}" role="tabpanel">
<!-- Loop over each record in $customer->getData -->
#foreach($customer->getData as $data)
{{ $data->comment }}
#endforeach
</div>
#endforeach
I have this relation defined in one of my models. It is the simplest possible case.
use \App\Models\Related;
public function entities()
{
return $this
->belongsToMany(Entity::class, 'entity_related', 'related_id', 'entity_id');
}
Now, I want to create a relation which gets only one model from the table.
Current solution
I just defined the same relation, but with ->take(1). Crude, but it works.
The downturn of this solution is that I need to do a foreach loop to get my desired single model.
use \App\Models\Entity;
public function firstOfEntities()
{
return $this
->belongsToMany(Entity::class, 'entity_related', 'related_id', 'entity_id')
->take(1); // <---
}
TO DO
How to properly define a relation which gets only one (just any) model instance, instead of creating a collection?
The desired usage
With the above completed I want to be able to use the single model in my template file inside a foreach loop:
#foreach($object as $o)
<h2>{{ $o->singleEntity->name }}</h2>
<p>{{ $o->singleEntity->description}}</p>
#endforeach
You could define an accessor to get your first element:
/** MyModel.php */
use \App\Models\Entity;
// Your current relationship
public function entities()
{
return $this
->belongsToMany(Entity::class, 'entity_related', 'related_id', 'entity_id');
}
// the accessor
public function getFirstEntityAttribute()
{
return $this->entities()->first();
}
Then in your controller:
/** MyModelController.php */
$model = MyModel::find(1);
$entity = $model->first_entity;
Check the docs related to this topic.
Hey i have three table like this
--table plan--
id
name
....
----table letter---
id
plan_id
....
---table person----
id
plan_id
name
.....
Model i have :
---Model plan---
class plan extends Model
{
protected $table = 'plan';
public function letter(){
return $this->hasOne('App\letter');
}
public function person(){
return $this->hasMany('App\person');
}
}
--Model person--
class person extends Model
{
public function plan(){
return $this->belongsTo('App\plan');
}
}
--Model letter--
class letter extends Model
{
public function plan(){
return $this->belongsTo('App\plan');
}
}
And in controller i write code like this :
$letter = letter::find($id) // $id from url parameter and it's work
return view('letter',['letter' => $letter]);
Nah in view i wanna acces person name from letter model as distinct , so i write code like this
{{ #foreach ($letter->plan()->person()->groupBy('name')->get) as $person }}
but it return error like this :
Call to undefined method Illuminate\Database\Query\Builder::person()
Where is my mistake(s)?
There is a difference between $letter->plan() and $letter->plan. If you call it like a method, Laravel will return the Query Builder. If you call it like an attribute Laravel will return the model from that relation.
So you're trying to call your model on the Query Builder, which is a method that doesn't exists and creates the error. This will fix your problem:
$letter->plan->person()->groupBy('name')->get()
In your controller you can do:
$letter = letter::find($id) // $id from url parameter and it's work
$persons = $letter->plan->person()->groupBy('name')->get();
return view('letter', compact('letter', 'persons'));
And in your view:
#foreach($persons as $person)
The table distributor_requisition_items has the columns :requisition_id,product_id ,measuring_unit_id,quantity
In the model DistributorRequisition.php, I have:
class DistributorRequisition extends Model
{
public function items()
{
return $this->belongsToMany('App\Product','distributor_requisition_items',
'requisition_id','product_id')->withPivot('measuring_unit_id','quantity');
}
}
In the controller I have :
class CurrentInventoryController extends Controller
{
public function distributor_inventory(Request $request)
{
$distributor_requisition=DistributorRequisition::get();
$distributor_requisitions_all= $distributor_requisition->items;
// this line produces an error
return view('admin.current_inventory.distributor-inventory',
compact('distributor_requisitions_all','stores',.....));
}
}
In the distributor-inventory.blade.php, I have :
#foreach($distributor_requisitions_all as $aP)
{{$aP->items->measuring_unit_id}}
#endforeach
I get the following error :
Exception in Collection.php line 1527: Property [items] does not exist on this collection instance.
in Collection.php line 1527 at Collection->__get('items') in CurrentInventoryController.php line 64
And the line 64 in the controller has :
$distributor_requisitions_all= $distributor_requisition->items;
So how to use the eloquent model method items in the controller so that I can access the distributor_requisitions_all variable in the view to extract values from it ?
use with() method instead of get().
public function distributor_inventory(Request $request)
{
$distributor_requisition=DistributorRequisition::with('items');
return view('admin.current_inventory.distributor-inventory', ['distributor_requisitions_all' => $distributor_requisition]);
}
$distributor_requisition=DistributorRequisition::get(); is a collection of Model DistributorRequisition, while items is a method inside the model that is why it is giving you an error .. you can't directly call a Model method from a collection .. and to solve your issue what you have to do is
in your function
public function distributor_inventory(Request $request)
{
$distributor_requisition=DistributorRequisition::get();
return view('admin.current_inventory.distributor-inventory',
compact('distributor_requisition','stores',.....));
}
and in your view
#foreach($distributor_requisition as $aP)
// {{ $aP->items->measuring_unit_id }}
// that will give you error since $aP->items is another collection of many model item what you can do is add another loop
#foreach($aP->items as $item)
$item->pivot->measuring_unit_id }}
#endforeach
#endforeach
As I was working on a Laravel project I noticed a recurring pattern in my models:
Model A hasMany Model B
Model B can optionally be hidden
Model B can optionally be sorted
Examples:
A gallery has pictures. You can choose not to display a picture or you can choose to sort them.
A "view our team" page. You can choose not to display certain employees or you can choose to sort them.
A slider on the home page. You can choose not to display certain images or you can choose to sort them.
I've been implementing all of these as follows:
class ModelA extends Model {
function modelBs() {
$this->hasMany('modelB');
}
}
class modelB extends Model {
protected $fillable = ['visible', 'order'];
function modelA() {
$this->belongsTo('modelA');
}
}
I'm also repeatedly re-implementing (copying/pasting) the code for displaying these in blade templates:
#foreach( $modelA->modelBs()->sortBy('order') as $modelB )
#if( $modelB->visible )
<li>{{ $modelB->output() }}</li>
#endif
#endforeach
And in the admin panel I have to repeatedly re-implement (copy/paste) the the jQuery-UI sortable widget for modifying the order, serializing your decision, and submitting it to the server then saving this order (via updating every Model B's order appropriately)
It's getting out of hand, and I remembered the adage from Laracasts:
If you find yourself using copy and paste, there's probably a better way to do it
As I tried to think of a better solution, this is the first relationship that I imagined:
Model A hasMany SortThing
SortThing morphsTo sortable
Model B hasOne sortable
This way I know that any SortThing can be sorted or hidden, and a SortThing can reference any sortable object (pictures, employees, slider panels, etc)
The problem is doing this doesn't really make my code any more DRY:
class ModelA extends Model {
function modelBs() {
$this->hasMany('SortThing');
}
}
class modelB extends Model {
function sortable() {
$this->morphsOne('SortThing', 'sortable');
}
}
class SortThing extends Model {
protected $fillable = ['visible', 'order'];
function sortable() {
$this->morphTo();
}
}
#foreach( $modelA->modelBs()->sortBy('order') as $modelB )
#if( $modelB->visible )
<li>{{ $modelB->sortable->output() }}</li>
#endif
#endforeach
I've added an extra class and necessitated the sortable-> in my output and I'm still copying/pasting code.
Any advice on how to clean up my code would be appreciated. Still a bit of a Laravel newb.
Bonus points if the resulting relationship doesn't require me to update 18 database rows when objects are re-ordered as this could potentially lead to some ugly overhead as the lists get really long.
Update
Attempting #gpopoteur's answer below (after fixing the typo in his declaration of the renderItems function) I got the following error:
[2015-03-24 13:08:52] production.ERROR: exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'App\Slider and App\HasSortableItemsTrait define the same property ($sortItemClass) in the composition of App\Slider. However, the definition differs and is considered incompatible. Class was composed' in /var/www/example.com/app/Slider.php:26
Slider.php looks like so:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Slider extends Model {
use HasSortableItemsTrait;
protected $sortItemClass = 'App\SliderPanel';
//
public function sliderable() {
return $this->morphTo();
}
public function panels() {
return $this->hasMany('App\SliderPanel');
}
public function output() {
$panels = $this->panels();
return "Test?";
}
} // Line 26
and HasSortableItemsTrait.php looks like so:
<?php namespace App;
trait HasSortableItemsTrait {
protected $sortItemClass; // Also tried $sortItemClass = ''; and $sortItemClass = null;
public function items() {
$this->hasMany($this->sortItemClass)->sortyBy('order')->where('visible', '=', true);
}
public function renderItems($htmlTag = '<li>:item</li>') {
$render = '';
foreach( $this->items() as $item ){
$render .= str_replace($item->render(), ':item', $htmlTag);
}
return $render;
}
}
Update 2
I've figured out that commenting out the following line fixes my issue:
protected $sortItemClass;
Of course I still have to make sure anything using the trait defines $sortItemClass or it will fail when calling items()
Now I'm getting a new error:
[2015-03-24 13:34:50] production.ERROR: exception 'BadMethodCallException' with message 'Call to undefined method Illuminate\Database\Query\Builder::sortBy()' in /var/www/example.com/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:1992
I double-checked the Laravel docs and I'm like 90% sure that sortBy should be a valid method on query builders...
You could try adding everything in a trait, and then just use SortableTrait; on your Model. And then setting the class as a protected attribute of the other Sortable items that it has many.
!!! Code below is not tested !!!
Lets define a trait for the Class that has sortable Items.
trait HasSortableItemsTrait {
// define this in your class
// protected $sortItemClass
public function items() {
$this->hasMany($sortItemClass)->orderBy('order');
}
public function renderItems($htmlTag = '<li>:item</li>']) {
return $this->items->map(function($item) use ($htmlTag) {
if( $item->visible ){
return str_replace($item->render(), ':item', $htmlTag);
}
});
}
}
Another trait to use on the items that are sortable.
trait IsSortableTrait {
// define this in your class
// protected $sortItemParent
function items() {
$this->belongsTo($sortItemParent);
}
function render(){
return $this->output();
}
}
Lets do an example with a Gallery that has Sortable Photos. This is how the App\Gallery should look like:
class Gallery extends Model {
use HasSortableItemsTrait;
protected $sortItemClass = 'App\Photo';
}
And this is how the App\Photo class would look like:
class Photo extends Model {
use IsSortableTrait;
protected $sortItemParent = 'App\Gallery';
}
Then you just need to fetch the Item that has many sortable items, in this case the gallery.
$gallery = Gallery::find(1);
And in the view you just need to call the renderItems() method of the view.
{{ $gallery->renderItems() }}
I made the renderItems method be able to receive how you want to wrap what the $item->render() will be giving as output. For example, between <p></p> you just have to call the method like this:
{{ $gallery->renderItems('<p>:item</p>') }}