I made a category tree and I need to pass one parameter to relation, I can't pass them.
public function Child()
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
but I want to use variable to pass in relation look like this.
public function Child()
{
return $this->hasMany(Category::class, 'parent_id', 'id')->where(['owner_id' => $this->ownerId]);
}
then I try to use variable and receive nothing, but if I use hardcoded value then works well. Please help
$models = App\{YourMainModel}::with(['Child' => function ($query) use ($this) {
$query->where(['owner_id' => $this->ownerId]);
}])->get();
You will need to add a constructor to your Child model (which extends the class Model).
private ownerId;
public function __construct(int ownerId)
{
parent::__construct($attributes);
$this->ownerId = $ownerId;
}
Then you can access this throughout your class.
public function child()
{
return $this->hasMany(Category::class, 'parent_id', 'id')->where('owner_id', $this->ownerId);
}
You would do this if every time you wanted to instantiate your class Child, you would have to give it an owner:
$ownerId = 5;
$child = new Child($ownerId);
Alternatively, you could pass a parameter directly to that function from wherever you call it, like:
public function childWithOwner(int $ownerId)
{
return $this->hasMany(Category::class, 'parent_id', 'id')->where('owner_id', $ownerId);
}
And you would call it: $this->childWithOwner(4);
As a tip I would encourage you to start your function names with a lowercase letter.
Related
i trying to load all rows from a model without the relationship.
The attributes $with it not event set on my Event model but when i do
$events = Event::all();
all my relationship are loaded, and i can see all the query with the dbquerylog.
i don't understand why theses relationship are loaded,
Please help me !
Thanks you.
I'm using Laravel 8.
here's an example.
class Event extends Model {
public function items() {
return $this->hasMany(Item::class);
}
public function items2() {
return $this->hasMany(Item2::class);
}
public function items3() {
return $this->hasMany(Item3::class);
}
public function items4() {
return $this->hasOne(Item4::class);
}
}
$events = Event::all();
If you have a single instance of a model object, you can do:
$obj->withoutRelations();
As laravel documentations says you can use without: https://laravel.com/docs/8.x/eloquent-relationships
Model
protected $with = ['item1','item2','item3','item4'];
Controller
$events = Event::without(['item1','item2','item3','item4'])->get();
I met this problem one day, and it turned out that I was using relation in scope method. Because of this relation values were added to response.
Check out this example:
class Event extends Model {
public function items() {
return $this->hasMany(Item::class);
}
[...]
public function scopeItemsGreen() {
return $this->items->every(function ($item) {
return $item->color == 'green';
});
}
Problem: I have accessor in Size.php model which is called in relationship with Item.php Model, in API i need the accessor to work, but in other controllers i want to disable the accessor. I have removed unnecessary/unrelated code from all files.
What i want to do:
In ItemControllerAPI i need accessor to work, but i want to disable accessor in other controllers.
I have already seen:
I have already seen these links but didn't work for me.
1: https://laracasts.com/discuss/channels/eloquent/accessor-on-demand-but-not-on-every-results
2: Create dynamic Laravel accessor
3: https://laracasts.com/discuss/channels/general-discussion/eager-load-accessors
Size.php (Model)
class Size extends Model
{
protected $appends = ['addons'];
public function items()
{
return $this->belongsToMany('App\Item', 'items_sizes')->withPivot('price');//->withTimestamps();
}
public function getAddonsAttribute($value)
{
$addon = Addon::where('addons.category_id', $this->category_id)
->where('size_id', $this->id)
->get();
return $addon;
}
}
Item.php (Model)
class Item extends Model
{
public function options()
{
return $this->belongsToMany('App\Option', 'items_options')->withTimestamps();
}
public function sizes()
{
return $this->belongsToMany('App\Size', 'items_sizes')->withPivot('price');//->withTimestamps();
}
}
ItemControllerAPI.php (Controller)
class ItemControllerAPI extends BaseControllerAPI
{
public function show($id)
{
// If i call the Size model directly by using setAppends([]) its working fine,
// its removing the appended array from Size model
// $size = Size::all();
// $size->each->setAppends([]);
// return $size;
// If i use it with relationship it won't work.
// $itemSingleQuery = Item::with(['sizes' => function($query)
// {
// Doesn't work
// $query->setAppends([]);
// Doesn't work
// $query->each->setAppends([]);
// }])
// ->with('options')
// ->where('id', $id)
// ->get();
// query for getting data with relationships
$itemSingleQuery = Item::with('sizes')
->with('options')
->where('id', $id)
->get();
return $this->respondSuccess('content found', $itemSingleQuery);
}
}
I've found out how to do this:
After getting the entire collection we need to call setAppends() in each model, which contains appends, to add or remove them before serialization to array or JSON.
$itemSingleQuery = Item::with('sizes')->get();
$itemSingleQuery->each(function ($item) {
$item->sizes->each->setAppends(['addons']);
});
I would suggest that you set $appends when you need it:
$itemSingleQuery->pluck('sizes')->collapse()->each->setAppends(['addons']);
This question was already asked here but it received no answer. Now I face the same problem but in laravel 5.4. I have a model Book, a model ReadingSession and a model Comment. A book has many reading sessions and has many comments but the reading session can also have comments. So I have my relations defined like this:
Book.php
protected $with = [
'author',
'readingSessions',
'userRating',
'ratings',
'comments'
];
public function users()
{
return $this->belongsToMany(User::class, 'user_book');
}
public function author()
{
return $this->belongsTo(Author::class);
}
public function allReadingSessions()
{
return $this->hasMany(ReadingSession::class);
}
public function readingSessions()
{
return $this->hasMany(ReadingSession::class)
->where('user_id', Auth::user()->id);
}
public function ratings()
{
return $this->hasMany(Rating::class);
}
public function userRating()
{
return $this->hasMany(Rating::class)
->where('user_id', Auth::user()->id);
}
public function comments()
{
return $this->morphMany('App\Models\Comment', 'commentable');
}
ReadingSession.php
protected $with = ['comments'];
public function user()
{
return $this->belongsTo(User::class);
}
public function book()
{
return $this->belongsTo(Book::class);
}
public function comments()
{
return $this->morphMany('App\Models\Comment', 'commentable');
}
Comment.php
public function commentable()
{
return $this->morphTo();
}
These seems to create an infinite loop. Can anyone hint me on what I'm doing wrong?
The main reason you might have an infinite loop there is if you are trying to load automatically a relationship that in turn tries to do the same with the previous model.
Putting it into an example:
Book.php
protected $with = [
'author',
];
public function author()
{
return $this->belongsTo(Author::class);
}
Author.php
protected $with = [
'books',
];
public function books()
{
return $this->hasMany(Book::class);
}
In this case, every time you fetch an author it will fetch automatically his books that in turn will try to fetch the author and on and on...
One other thing that might happen and it's harder to realize is when using the $appends property on some accessors. If you are trying automatically had a variable into a model through the $appends and if that accessor fetches a relation or uses a relation in some way you might get an infinite loop again.
Example:
Author.php
protected $appends = [
'AllBooks',
];
public function books()
{
return $this->hasMany(Book::class);
}
public function getAllBooksAttribute() {
return $this->books->something...
}
In this case, every time the app tries to resolve your Author model it will fetch the books, that in turn will fetch the Author, that in turn will fetch the books again and on and on...
From your snippets, is not clear what is causing the problem but this answer might give some leads where to search for it.
To solve it, you might remove the relation from the $with and load it manually: $author->load('books') or Author::with('books')->where...
You can also load a relation of a relation in this way, for example: $author->load('books', 'books.comments') or Author::with('books', 'books.comments')->where...
It all comes down what you are trying to achieve. So you have to evaluate what and what not you should auto-load.
Be careful when loading automatically relations on your models and when adding accessors to $appends, especially if they use relations. It is an awesome feature but can bite hard sometimes.
I am writing a website for photo posts and I have these functions relating likes (they determine if the user is liking the specific post or not)
Post Model:
public function likes()
{
return $this->hasMany('Like');
}
public function isLiked()
{
return $this->likes()->where('user_id', Auth::user()->id);
}
Post Controller function for example:
public function postsByType($type)
{
if($this->user){
$posts = Post::with('isLiked')->where('type', '=', $type)->paginate(12);
} else {
$posts = Post::where('type', '=', $type)->paginate(12);
}
return $posts;
}
Is there any way to return null in MODEL function when user is not logged in, without running a query?
I want to avoid writing that if in post controller
I thought about the following solution but it's not working...
public function isFollowing()
{
return $this->setRelation('isFollowing', null);
}
getting this error:
Call to undefined method Illuminate\Database\Query \Builder::addEagerConstraints()
Since you probably always want to fetch the relation (except if there's no user logged in) I suggest you do something like this in your model:
(I also renamed the relationship to liked, you'll see later why)
public function newQuery(){
$query = parent::newQuery();
if(Auth::check()){
$query->with('liked');
}
return $query;
}
Now every time a query is run with the model with('isLiked') will be added if the user is logged in.
One problem remains though. If you access isLiked the query will be run anyways. And even for every post because it's not eager loaded. You can fix that by adding an attribute accessor:
public function getIsLikedAttribute(){
if(Auth::guest) return false;
return ! $this->liked->isEmpty();
}
So in your view you can just do this:
#if($post->isLiked)
Note: It would be nicer to move the things inside newQuery() to a global scope. Make sure to check out how to do that in the documentation if you're interested.
Here's an example with a scope. Create a class, let's call it LikedScope:
class LikedScope implements Illuminate\Database\Eloquent\ScopeInterface {
public function apply(Builder $builder, Model $model){
if(Auth::check()){
$builder->with('liked');
}
}
public function remove(Builder $builder, Model $model){
}
}
And then add it to your model:
public static function boot(){
parent::boot();
static::addGlobalScope(new LikedScope);
}
I have variables that are created and used on my model that I need to be able to use on my controller how is that accomplished?
Edit:
Controller: http://pastebin.com/jhAwAVa6
Model: http://pastebin.com/9xXRyYAa
It's unclear from your question, what exactly you want to do.
If it is about accessing model properties, the right way is using accessor methods:
class Model extends CI_Model{
private $name;
public function getName() {return $this->name; /*any other logic here*/}
public function setName($value) {$this->name= $value; /*any other logic here*/}
}
You can not pass the variable from a model to controller.
You can access public variables of a model through a controller.
echo $this->model_name->variable_name;
Model (my_model)
function useful_info()
{
$data = new stdClass();
$q = $this->db->get('users');
$data->users = $this->db->result();
$data->date = date('Y-m-d');
$data->info = array('whatever','more','anything');
return $data;
}
Controller
function index()
{
$info = $this->my_model->useful_info();
foreach($info->users as $user)
{
echo $user->id;
}
echo $info->date;
if($info->info[0] == 'whatever')
{
// do something
}
}
You don't have to create an object (it can be a string, T/F, array, etc), but you usually need to return something from your model and library functions. And you can access what you return by returning it to a variable $info = $this->my_model->useful_info();