In my Menu controller I have a method which should display the specified record and all its children.
I have two models: MenuItem and MenuVariation, My items will have many variations as outlined in my methods:
MenuItem model
public function variant()
{
return $this->hasMany('App\MenuVariation');
}
MenuVariation model
public function item()
{
return $this->belongsTo('App\MenuItem', 'menu_item_id');
}
Now in my controller I have the following method:
public function show($id)
{
$item = MenuItem::findOrFail($id);
return $item;
}
...which currently only shows the item record but not its variations, so I have changed the code like this...
public function show($id)
{
$item = MenuItem::findOrFail($id)->with('variant')->get();
return $item;
}
but this oddly return ALL items and their variations.
Could someone help me get this working as desired? I would like to still utilise FindOrFail on the Item record, but it should also retrieve any variants (if found).
findOrFail will initiate the query, so you want to switch the order and put with in-front of it. Then use findOrFail. See Below:
public function show($id)
{
$item = MenuItem::with('variant')->findOrFail($id);
return $item;
}
There is also no need for get when you do that.
Related
I am trying to retrieve the data on my wishlist table, for a particular user, so far it only retrieves the first data on the table, just returning one array instead of the three in the table with same user id
public function getWishlistByUserId($id){
$wishlists = Wishlist::where('userId', $id)->get();
foreach($wishlists as $wishlist){
$products = Product::where('id', $wishlist->productId)->get();
return $products;
}
}
It happens because the foreach loop returns a value during the first iteration. Place your return statement outside the loop. Also you could improve your performence by making use of relationships.
An example could be:
// Product.php
public function wishlists()
{
return $this->hasMany(Wishlist::class);
}
// Your method
public function getWishlistByUserId($id)
{
return Product::whereHas('wishlists', function ($query) use ($id) {
$query->where('userId', $id);
});
}
Ideally this is n+1 situation
So i will suggest to use laravel relationship like:
in your whishlist model
public function product(){
return $this->hasMany(Product::class,'productId','id');
}
get data with relationship
public function getWishlistByUserId($id){
$wishlists = Wishlist::with('product')->where('userId', $id)->get();
}
I was finally able to get it working this way, i just pushed the result into an array, and then returned it outside the loop, thanks everyone for your help
public function getWishlistByUserId($id){
$wishlists = Wishlist::where('userId', $id)->get();
$wishlist = [];
foreach($wishlists as $wish){
$product = Product::where('id', $wish->productId)->get();
array_push($wishlist, $product);
}
return $wishlist;
}
I have the following 2 models in a morphToMany relationship: Company and Tag
public function tags(){
return $this->morphToMany(Tag::class, 'taggable');
}
public function companies(){
return $this->morphedByMany(Company::class, 'taggable');
}
and I have the following working action to save 1 tag to 1 company:
public function addTag($request){
$company = Company::find($request->company_id);
$company->tags()->syncWithoutDetaching($request->tag_id);
return response()->json($company->tags, 201);
}
now I'm trying to do the same thing with many companies and many tags and it is not working. I've tried "syncWithoutDetaching", "saveMany", passing in an array of integers or strings, I keep getting this error:
BadMethodCallException: Method Illuminate\Database\Eloquent\Collection::tags does not exist. in file /home/vagrant/.../vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php on line 100
This is the function I am using:
public function multiCompanyAddTags($request){
$tags = $this->createManyRecords('App\AppSections\CRM\Models\Tag', $request->new_tags);
$tagIDs = $tags->map(function ($item, $key) { return $item->id;
});
foreach ($request->companies as $key=>$id)
{
$company = Company::with(['notes','tags','contacts','tasks'])->find($id);
$company->tags()->syncWithoutDetaching($tagIDs);
return response()->json($company, 201);
}
}
Someone pointed me to the answer: When calling $company = Company::with... in my loop, the call returns a collection and not the company.
I then need to call the first item of the collection to access the company, like so:
$company[0]->tags()->syncWithoutDetaching($tagIDs);
I try to get my categories base on their status and i have scope below in my category model:
public function scopeOfStatus($query, $status)
{
return $query->where('status_id', $status);
}
And my controller is like:
public function finder() {
$findercategories = Category::OfStatus('Active')->get();
return response()->json($findercategories);
}
My route is like:
Route::get('/finder','frontend\SearchController#finder');
But i get blank page as result. Any idea?
update
if i use $findercategories = Category::ofStatus(1)->get(); i get the result that i want but it's static not dynamic :\
Get the Id's from the statuses table with respect to the searched value and use the Id's to get the Category
public function scopeOfStatus($query, $status)
{
$statuses = Status::where('title', $status)->pluck('id'); // Or relevant column name
return $query->whereIn('status_id', $statuses);
}
and in your Controller you could do so
public function finder() {
$findercategories = Category::ofStatus('Active')->get();
return response()->json($findercategories);
}
I have 3 models: Shop, Products and Tags. Shop and Products are in one to many relation, and Products to Tags many to many.
I want to grab for each Shop all unique Tags (since many products can have same tags).
class Shop extends Eloquent {
public function products() {
return $this->hasMany('Product');
}
}
class Product extends Eloquent {
public function shop() {
return $this->belongsTo('Shop');
}
public function tags() {
return $this->belongsToMany('Tag');
}
}
class Tag extends Eloquent {
public function products() {
return $this->belongsToMany('Product');
}
}
One of the solutions that I came up with is following. Problem is that I don't get unique tags. There is a solution to put another foreach loop to go thru tags array and compare id in tag object. I would like to optimize a little bit, what do you think is better/cleaner solution?
class Shop extends Eloquent {
...
public function getTagsAttribute() {
$tags = array();
foreach($this->products as $product)
{
foreach ($product->tags as $tag)
{
$tags[] = $tag;
}
}
return $tags;
}
}
#WereWolf's method will work for you, however here's a trick that will work for all the relations:
$shop = Shop::with(['products.tags' => function ($q) use (&$tags) {
$tags = $q->get()->unique();
}])->find($someId);
// then:
$tags; // collection of unique tags related to your shop through the products
Mind that each of the $tags will have pivot property, since it's a belongsToMany relation, but obviously you don't rely on that.
Probably you may try this:
$tags = Tag::has('products')->get();
This will return all the Tags that's bound to any Product. If necessary, you may also use distinct, like this, but I think it's not necessary in this case:
$tags = Tag::has('products')->distinct()->get();
Update: Then you may try something like this:
public function getTagsAttribute()
{
$shopId = $this->id;
$tags = Tag::whereHas('products', function($query) use($shopId) {
$query->where('products.shop_id', $shopId);
})->get();
return $tags;
}
I have three tables - Campaigns, Actions and Activists. A Campaign has many Actions, and an Action belongs to both an Activist and a Campaign.
Each action has a client_id (from the client_id of the campaign it belongs to), so when a client views a list of activists, they should only see those who have taken an action on one of their campaigns.
Likewise, when viewing an individual activist, they should only see those actions related to their campaigns.
Models
Campaign.php
public function actions()
{
return $this->hasMany('Action');
}
Action.php
public function campaign()
{
return $this->belongsTo('Campaign', 'campaign_id');
}
public function activist()
{
return $this->belongsTo('Activist', 'activist_id');
}
Activists.php
public function actions()
{
return $this->hasMany('Action');
}
Controllers
ActivistsController.php
public function index()
{
$activists = Activist::with('actions')->whereHas('actions', function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
}))->get();
foreach ($activists as $activist)
{
$activist->total = $activist->actions()->count();
}
}
public function getActivist($id)
{
$activist = Activist::with('actions')->whereHas('actions', function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
})->find($id);
$activist->total = $activist->actions()->count();
}
I'm seeing the following:
On the /activists page, I'm correctly seeing only those activists who have taken an action related to my client_id, but also every action they've taken. Likewise, count() returns a full count of all the activists' actions.
On the /activists/{id} page, it correctly returns null if the activist hasn't taken any actions related to my client_id, but where they have, I again see all of their actions and a full count.
AFL. There's something blinding obvious I'm missing, right?
Thanks.
[edit] Updated to add:
Using the client_id filter on both with and whereHas rectifies the 'all actions appearing regardless' issue, but the count issue remains (and I'm not sure this is remotely the right way to improve this):
ActivistController.php
public function index()
{
$filter = function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
};
$activists = Activist::with(array('actions' => $filter))
->whereHas('actions', $filter)
->get();
}
public function getActivist($id)
{
$filter = function($q) {
$user = Sentry::getUser();
$q->where('client_id', $user->client_id);
};
$activist = Activist::with(array('actions' => $filter))
->whereHas('actions', $filter)
->find($id);
}
I've solved this now, but for reference:
$activist->actions()->count()
This, obviously in hindsight, ignores any of the prior queries and simply counts data returned from the actions() method as defined in the activist model.
I should have provided an alternate method in the model that includes the appropriate where function, like so:
public function actionsClient($id)
{
return $this->hasMany('Action')->where('client_id', $id);
}
Meaning the count could then be invoked with:
$activist->total = $activist->actionsClient($id)->count();
for a single campaign and
foreach ($activists as $activist)
{
$activist->total = $activist->actionsClient($activist->id)->count();
}
on the index. I'd previously tried this, but as described here - How to access model hasMany Relation with where condition? - relations must be described in camelCase (actions_client > actionsClient).
In my usage this worked for me:
$clients = Profile::select('id', 'name')->orderBy('initial')->whereHas('type', function ($query) {
$query->where('slug', 'client');
})->office()->pluck('name', 'id');
You already have the instance of $activist eager loading their actions, meaning you already know the actions of the activist beacause they are already in the instance, so why call actions() again instead of just doing this:
$activist->actions->count()