How to create self referential relationship in laravel? - laravel-4

I am new to Laravel. I Just want to create a self referential model. For example, I want to create a product category in which the field parent_id as same as product category id. How is this possible?
Model Shown below
class Product_category extends Eloquent
{
protected $guarded = array();
public static $rules = array(
'name' => 'required',
'parent_id' => 'required'
);
function product_category()
{
return $this->belongsto('Product_category','parent_id');
}
}
It results Maximum function nesting level of '100' reached, aborting! Error

You can add a relation to the model and set the custom key for the relation field.
Update:
Try this construction
class Post extends Eloquent {
public function parent()
{
return $this->belongsTo('Post', 'parent_id');
}
public function children()
{
return $this->hasMany('Post', 'parent_id');
}
}
Old answer:
class Post extends Eloquent {
function posts(){
return $this->hasMany('Post', 'parent_id');
}
}

Your model is not at fault for producing the "maximum function nesting level of '100' reached" error. It's XDebug's configuration; increase your xdebug.max_nesting_level.
The following is from a 2015 post by #sitesense on laracasts.com:
This is not a bug in Laravel, Symfony or anything else. It only occurs when XDebug is installed.
It happens simply because 100 or more functions are called recursively. This is not a high figure as such and later versions of XDebug (>= 2.3.0) have raised this limit to 256. See here:
http://bugs.xdebug.org/bug_view_page.php?bug_id=00001100
EDIT: In fact the latest Homestead provisioning script already sets the limit to 250. See line 122 here:
https://github.com/laravel/settler/blob/master/scripts/provision.sh#L122
So the addition of xdebug.max_nesting_level = 250 to php.ini should do it.

I've added a little more to the code based on your comments trying to access the parent!
class Person extends \Eloquent {
protected $fillable = [];
var $mom, $kids;
function __construct() {
if($this->dependency_id<>0) {
$this->mother->with('mother');
}
}
public function children() {
$children = $this->hasMany('Person','dependency_id');
foreach($children as $child) {
$child->mom = $this;
}
return $children;
}
public function mother() {
$mother = $this->belongsTo('Person','dependency_id');
if(isset($mother->kids)) {
$mother->kids->merge($mother);
}
return $mother;
}
}
Then you can access the parent from the child with eager loading, see more here: http://neonos.net/laravel-eloquent-model-parentchild-relationship-with-itself/

you can refer self, using $this
class Post extends Eloquent {
function posts(){
return $this->hasMany($this, 'parent_id');
}
}

Take a look at my answer here.
The key is this code below in Model.php
public function children()
{
return $this->hasMany(Structure::class, 'parent_id')->with('children');
}

Related

Method addEagerConstraints does not exist in Laravel

I'm building a small application on Laravel 5.6 where I'm having a Company model where I am having a hasMany relation to model FinancialAndRisk something like this:
class Company extends Model {
use SoftDeletes;
protected $fillable = [
'name', 'slug', 'establishment', 'parent_id', 'website', 'updates'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'created_at','updated_at','deleted_at'
];
public function financial()
{
return $this->hasMany('Noetic\Plugins\Conxn\Models\Company\FinancialAndRisk', 'company_id');
}
public function latestFinancial()
{
return $this->hasMany('Noetic\Plugins\Conxn\Models\Company\FinancialAndRisk', 'company_id')->latest()->first();
}
}
Now at some places I want the latest financial report so I made a function latestFinancial
But when in my controller I do something like this:
public function index()
{
$companies = Company::with('latestFinancial')->get();
return response()->json(['companies' => $companies], 200);
}
I get an error:
{
"message": "Method Illuminate\\Database\\Query\\Builder::addEagerConstraints does not exist.",
"exception": "BadMethodCallException",
"file": "D:\\xampp\\htdocs\\conxn\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Query\\Builder.php",
"line": 2671,
How can I resolve this.
On your model you should define that you want an eager load with only One result, so instead of saying hasMany you should do hasOne: Also just use ->latest(); first() isn't necessary here
public function latestFinancial()
{
return $this->hasOne('Noetic\Plugins\Conxn\Models\Company\FinancialAndRisk', 'company_id')->latest();
}
Then it will only give you the latest record associated
The problem is ->first() because it executes the query. Remove it and use HasOne:
public function latestFinancial()
{
return $this->hasOne('Noetic\Plugins\Conxn\Models\Company\FinancialAndRisk', 'company_id')->latest();
}
The problem for me was that i was using ->get() at the end of the query.
I removed it and my query was fine i.e
formally i did
public function priceCategoryEntities()
{
return $this->hasOne((new AppUsersVipPriceCategoriesEntity), 'price_category_id', 'id')->get();
}
But Now am using
public function priceCategoryEntities()
{
return $this->hasOne((new AppUsersVipPriceCategoriesEntity), 'price_category_id', 'id');
}
and the error is gone.

Trying to get property of non-object, but I can if I return it inmediatly

I have a good one here:
Im developing an API and im returning my values with the following code:
public function apigetDrugs(){
$arrayreturn= array();
foreach (Drug::all() as $drug){
$array=[
"pharma"=>$drug->pharma->name,
"id"=>$drug->id,
"code"=>$drug->code,
"CABMS_code"=>$drug->CABMS_code,
"CABMSDF_code"=>$drug->CABMSDF_code,
"name"=>$drug->name,
"concentration"=>$drug->concentration,
"presentation"=>$drug->presentation->name,
"container"=>$drug->container->name,
"previous_stock"=>$drug->previous_stock,
"added"=>$drug->added,
"added_transferred"=>$drug->added_transferred,
"exit"=>$drug->exit,
"exit_transferred"=>$drug->exit_transferred,
"extra"=>$drug->extra,
"user"=>$drug->user->name,
];
$arrayreturn[]=$array;
}
return $arrayreturn;
}
"Drug" model has relationships with Container, Presentation, User and Pharma. When I run my API I get the error "Trying to get property of non-object", so I decided to comment each line of code one by one and I found that I have the code running smoothly if I comment the pharma line, just like this:
public function apigetDrugs(){
$arrayreturn= array();
foreach (Drug::all() as $drug){
$array=[
//"pharma"=>$drug->pharma->name,
"id"=>$drug->id,
"code"=>$drug->code,
"CABMS_code"=>$drug->CABMS_code,
"CABMSDF_code"=>$drug->CABMSDF_code,
"name"=>$drug->name,
"concentration"=>$drug->concentration,
"presentation"=>$drug->presentation->name,
"container"=>$drug->container->name,
"previous_stock"=>$drug->previous_stock,
"added"=>$drug->added,
"added_transferred"=>$drug->added_transferred,
"exit"=>$drug->exit,
"exit_transferred"=>$drug->exit_transferred,
"extra"=>$drug->extra,
"user"=>$drug->user->name,
];
$arrayreturn[]=$array;
}
return $arrayreturn;
}
So I checked my relationships in the Pharma and Drug classes, nothing wrong that I can see with Drug:
class Drug extends Model
{
protected $guarded=[
"id",
"user_id"
];
use SoftDeletes;
protected $dates = ['deleted_at'];
//
public function user(){
return $this->belongsTo("App\User");
}
public function pharma(){
return $this->belongsTo("App\Pharma");
}
public function presentation(){
return $this->belongsTo("App\Presentation");
}
public function unit(){
return $this->belongsTo("App\Unit");
}
public function container(){
return $this->belongsTo("App\Container");
}
}
Nor with Pharma:
class Pharma extends Model
{
//
protected $guarded=[
"id"
];
public function drugs(){
$this->hasMany("App\Drug");
}
public function user(){
$this->belongsTo("App\User");
}
}
However I found something strange. I found this code will run without an issue and display perfectly the string that is intended to show:
public function apigetDrugs(){
$arrayreturn= array();
foreach (Drug::all() as $drug){
dd($drug->pharma->name);
}
}
And it will even work if I replace "dd" function with "echo". Hope to hear what you think about this issue soon.
Luis
Found my problem, one of my seeds had an invalid drug_id value.

How would I paginate these results in laravel?

I was reading this article to work out how to sort records in my database based on how many likes they have:
Laravel OrderBy relationship count
I came up with this which works:
$Book = Book::with('likes')->get()->sortByDesc(function($book_sort)
{
return $book_sort->likes->count();
});
Which is based upon this Book model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
public $timestamps = true;
protected $fillable = [
'title', 'author', 'category', 'featured', 'rating', 'description'
];
public function category()
{
return $this->hasOne('App\Cat', 'id', 'category_id');
}
public function likes()
{
return $this->belongsToMany('App\User', 'favourite_books')->withTimestamps();
}
public function total_likes()
{
return $this->likes()->count();
}
}
However now I am stuck on how I would paginate these results. Does anyone know?
Create manual pagination, try this:
$Book = Book::with('likes')->get()->sortByDesc(function($book_sort)
{
return $book_sort->likes->count();
});
$paginator = new Illuminate\Pagination\Paginator($Book, 10);
return view('pages.homepage', compact('paginator'))
sortBy() and sortByDesc() are working with collections only. orderBy() is working only with column name. Also, getting results, sorting and paginating them is a perfomance hit. It will also can eat all the memory.
So, the only solution I can see here is to use orderByRaw() or even raw query.

Laravel 5.1 Querying Relationship

Learning Laravel by building a stock portfolio app. Have models for Users, Accounts, Stocks, Options, and Transactions. I believe I have the relationships set up properly.
Question is how do I get a Users->Account->Transactions. I'm sure I could just do something in query builder but I was hoping for a more "eloquent" approach.
User
public function accounts()
{
return $this->hasMany('App\Account');
}
public function stocks()
{
return $this->belongsToMany('App\Stock');
}
public function options()
{
return $this->belongsToMany('App\Option');
}
public function transactions()
{
return $this->hasMany('App\Transaction');
}
Account
class Account extends Model
{
protected $fillable =
[
'name',
'broker'
];
public function user()
{
return $this->belongsTo('App\User');
}
public function stocks()
{
return $this->belongsToMany('App\Stock');
}
public function options()
{
return $this->belongsToMany('App\Option');
}
public function transactions()
{
return $this->hasMany('App\Transaction');
}
Transaction
class Transaction extends Model
{
protected $fillable =
[
'type',
'account_id',
'transaction_date',
'quantity',
'stock_id',
'option_id',
'amount',
'description'
];
public function user()
{
return $this->belongsTo('App\User');
}
public function stock()
{
return $this->belongsTo('App\Stock');
}
public function option()
{
return $this->belongsTo('App\Option');
}
public function account()
{
return $this->belongsTo('App\Account');
}
public function accounts()
{
return $this->hasManyThrough('App\Account', 'App\User');
}
}
Ultimately, I guess I would be looking for a total amount for each account a user may have. (User can have many accounts and each account would have many transactions, stocks and options.)
Thanks for any help or at least letting me know if I am going down the right road!!
If $user is a User object then $user->accounts is a collection of Eloquent models
You can load all of the accounts and their transactions with eager loading:
$user->load([
'accounts',
'accounts.transactions',
]);
But $user->accounts->transactions isn't something you can do because "transactions" is a relation on each individual Account object, not on the collection of account objects. So to get the transactions you'd loop through each account:
foreach ($user->accounts as $account) {
// do something with $account->transactions;
}
I highly don't recommend doing this, as #alexw mentioned in his comment, Eloquent objects are pretty large and having x many x many is an exponentially large amount of data you're loading into memory. But generally, that's how you'd access relations of relations.
You're better off using the query builder
The good news is that relations can make querying really easy! For example, you could do something like this instead:
Note: in Laravel 5.2, the lists method has been replaced with pluck
$user->load(['accounts']);
foreach ($user->accounts as $account) {
$amounts = $account->transactions()->lists('amount');
$total = $amounts->sum();
// - or -
$query = $account->transactions()
->select(\DB::raw('SUM(amount) AS total'))
->first();
$total = $query->total;
}

Global filtering - how to use global scope in Laravel Eloquent

I have a published filter that I use for my articles. Guests can only view published articles, logged in users can view and apply filter (?published=0/1):
public function scopePublishedFilter($query)
{
if(!Auth::check()) $query->where('published', '=', 1);
else
{
$published = Input::get('published');
if (isset($published)) $query->where('published', '=', $published);
}
return $query;
}
I apply this in my ArticlesController:
public function index()
{
return View::make('articles.index', [
'articles' => Article::with('owner')
->with('category')
->with('tags')
->publishedFilter()
->get()
]);
}
And on the article relationships:
public function articles()
{
return $this->hasMany('Article')->publishedFilter();
}
But ideally I would like to only define it in the Article model itself, since it's easy to forget to include this filter when implementing new features or views.
How can I make sure that all returned articles from the Article model are run through this filter before returned?
UPDATE: Just use this: https://github.com/jarektkaczyk/laravel-global-scope for global scopes in L5+
Better way is a bit too long to paste it and works like SoftDeleting thing in the core.
Read this if you want it http://softonsofa.com/laravel-how-to-define-and-use-eloquent-global-scopes/
Short way: you need global scope for this. And here's how you do it in 2 steps (squashed a bit):
1 Create a class PublishedScope that implements ScopeInterface
class PublishedScope implements ScopeInterface {
public function apply(Builder $builder)
{
$table = $builder->getModel()->getTable();
$builder->where($table.'.published', '=', 1);
$this->addWithDrafts($builder);
}
public function remove(Builder $builder)
{
$query = $builder->getQuery();
$column = $builder->getModel()->getTable().'.published';
$bindingKey = 0;
foreach ((array) $query->wheres as $key => $where)
{
if ($this->isPublishedConstraint($where, $column))
{
unset($query->wheres[$key]);
$query->wheres = array_values($query->wheres);
$this->removeBinding($query, $bindingKey);
}
// Check if where is either NULL or NOT NULL type,
// if that's the case, don't increment the key
// since there is no binding for these types
if ( ! in_array($where['type'], ['Null', 'NotNull'])) $bindingKey++;
}
}
protected function removeBinding(Builder $query, $key)
{
$bindings = $query->getRawBindings()['where'];
unset($bindings[$key]);
$query->setBindings($bindings);
}
protected function addWithDrafts(Builder $builder)
{
$builder->macro('withDrafts', function(Builder $builder)
{
$this->remove($builder);
return $builder;
});
}
2 Boot that class in your Eloquent model by calling static::addGlobalScope(new AbcScope)
// the model
public static function boot()
{
parent::boot();
static::addGlobalScope(new PublishedScope);
}
If I were you I would use published_at column and check it for null instead of = 1, but that's up to you.
edit remove method updated - thanks to #Leon for pointing out unexpected behaviour, when using this scope together with SoftDeletingTrait. The problem is a bit deeper:
when you use this one with SoftDeletingScope or another one, that utilizes NULL or NOT NULL constraint and this scope is not the first one used (yes, order of use statements matters here), remove method will not work as expected. It will not remove any binding or not the one, that it should.
you can use trait and add your method or filter thing in booting method check the following
http://laravel.com/docs/4.2/eloquent#global-scopes

Resources