After eager loading attributes and their items I tried to sort the attributes without of luck.
public function __invoke(Category $category)
{
$category->load(['attributes']);
$category->attributes->load(['items' => function ($query) use ($category) {
$query->whereHas('productVariations.product.categories', function ($query) use ($category) {
$query->where('categories.id', $category->id);
});
}]);
foreach ($category->attributes as $attribute) {
$attribute->items = $attribute->items->sortByDesc('name');
}
dump($category->attributes->first()->items->pluck('name')); // Items are sorted
dd($category->attributes->toArray()); // Items are not sorted
}
Can you please tell me why is that and how can I sort them with collections (not Eloquent and MySql)?
Your problem is that you are not replacing the items in the collection itself. So I believe a map will do a better job here than a traditional foreach. So try this instead and let me know:
$attributes = $category->attributes->map(function($attribute) {
$attribute->items = $attribute->items->sortByDesc('name');
return $attribute;
});
dd($attributes);
Related
I am trying to build a Laravel Nova Filter for my Model "Question", based on data from two different tables. I have figured out the exact SQL query I would need to use, but cannot figure out how to use it in the apply() method of the Nova filter.
Table "questions" columns:
id, ...
Table "themes" columns:
id, question_id, theme_id
The SQL Query I am trying to use:
SELECT questions.*
From questions
Inner JOIN themes on questions.id=themes.question_id
WHERE themes.theme_id = 3 (value taken from the Nova Filter options)
My Filter in Laravel Nova QuestionsThemeFilter.php:
class QuestionsThemeFilter extends BooleanFilter
{
public $name = 'filter by theme';
public function apply(Request $request, $query, $value) {
// This is what I'm missing...
}
public function options(Request $request) {
$themes = Themes::all();
$themesArray = array();
foreach($themes as $item) {
$themesArray[$item['title']] = strval($item['id']);
}
return $themesArray;
}
}
So, how exactly should I do this?
Other question: Since I am a beginner in Laravel, how exactly should I start debugging this? Using dd() doesn't work (the filter just breaks) and I don't know how I can debug this. Is there a way to dump some values from the apply method (for example the $value)?
Thanks in advance for any help, highly appreciated :-D
Check the comments in the code
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
class QuestionsThemeFilter extends BooleanFilter
{
public function apply(Request $request, $query, $value) {
// Since you are using BooleanFilter $value will be an associate array with true/false value
$selectedThemes = Arr::where($value, function ($v, $k) {
return $v === true;
});
$selectedThemeIds = array_keys($selectedThemes)
// For debugging print some logs and check
Log::info($value)
Log::info($selectedThemeIds);
return $query->join('themes', 'questions.id', '=', 'themes.question_id')
->whereIn('themes.theme_id', $selectedThemeIds);
// If you are using normal filter then below code will be enough
/*
return $query->join('themes', 'questions.id', '=', 'themes.question_id')
->where('themes.theme_id', $value);
*/
}
public function options(Request $request) {
// You can use Laravel collection method pluck
return Themes::pluck('id', 'title')->all();
}
}
References:
Pluck
Boolean Filter
Arr::where
How can I paginate the result here and keep the sort order.
public function show(Language $language)
{
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}])->get()->sortByDesc('translations');
return view('language.show', compact('questions', 'language'));
}
Right now if I try to paginate by doing this, I get an error saying:
Call to undefined method Illuminate\Database\Eloquent\Builder::sortByDesc()
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}])->sortByDesc('translations')->Paginate(20);
I also tried this from this post: how to use pangination and sortByDesc fuction in laravel?
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}])->Paginate(20);
$questions->setCollection($questions->sortByDesc('translations'));
This sort of works because I get the paginated results in a sorted order but it's not what I want. I want all the questions to be first sorted by whether they have translations or not and then paginated.
Note, translations is not a column, it's just the result of the query so I can't use SQL as suggested in the post I linked.
The relationship in question model.
public function translations()
{
return $this->hasMany('App\QuestionTranslation');
}
Ordering the relationship will have no effect on the order of the main model. Relationships are retrieved via a second query which happens after the main model is retrieved.
You can join however:
public function show(Language $language)
{
$questions = Question::selectRaw('questions.*, translations.id')
->leftJoin('translations', function ($join) use ($language) {
$join->on('questions.id', '=', 'translations.question_id')
->andOn('translations.id', '=', $language->id);
})
->orderBy('translations.id') // NULLs should appear last
->paginate(20);
return view('language.show', compact('questions', 'language'));
}
private $limit = 10;
public function show(Language $language)
{
$questions = Question::with(['translations' => function($q) use ($language) {
$q->where('language_id', $language->id);
}]);
$data = $questions->paginate($this->limit);
$data->sortByDesc('translations');
return view('language.show', $data);
}
i have this code in my controller
if (request('tag')) {
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->Posts;
} else {
$Posts = Blog::paginate(10);
}
return view('Panel.Posts',['Posts' => $Posts]);
and this is my model function
public function tag(){
return $this->belongsToMany(Tags::class,'Post_tag','Post_id');
}
It's a web application I am creating and it's a blog.
I'm trying to create a category.
I want to paginate my search method but I can't do it.
If I use paginate method it says posts relation is not found in paginate.
I tried this:
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->paginate(10)->Posts;
and this
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->Posts->paginate(10);
Can any one help me to fix this?
try my code
$Posts = Blog::whereHas('tag', function (Builder $query) {
$query->where('name', request('tag'));
})->paginate(25);
you can yse where ha and query builder instans a relation in models
Make sure Tags has relation relation in model. Also use ->posts instead ->Posts, but anyway:
There is no point of using separate query if relation is defined or not. Instead you need combination of when and whereHas.
$posts = Blog::when(request('tag'), function ($q, $tagName) {
return $q->whereHas('tag', function ($q) use ($tagName) {
return $q->where('name', $tagName);
});
})->paginate(10);
When fill apply what is inside it if condifion will match. So it is just replace for "if". Docs https://laravel.com/docs/7.x/queries#conditional-clauses
WhereHas will check if relation model exists and allow you to use "where" (or other queries) on that relation. Docs https://laravel.com/docs/7.x/eloquent-relationships#querying-relationship-existence
If you wanna still use "if" nad not "when":
$posts = Blog::query();
if (request('tag')) {
$posts = $posts->whereHas('tag', function ($q) {
return $q->where('name', request('tag'));
});
}
$posts = $posts->paginate(10);
I've been looking at the newQuery eloquent model and best case uses and I can see the benefit in searching / filtering products based on a search page however is it possible to call the newQuery on only the Users related products?
For example, I have 2 models.
User
Product
User has many products and I have the relationship defined on the user model.
public function products() {
return $this->hasMany('App\Product');
};
Now, previously if I wanted to filter all the products and take user out of the scenario I could use:
$query = (new \App\Product)->newQuery();
if($request->get('category')){
$query->whereHas('category',function($q) use($request){
$q->where('category_id',$request->get('category'));
});
}
$products = $query->get();
This is great and I like this method and I now want to have a similar functionality on only users products.
For example, id like to:
$products = (Auth::user()->products)->newQuery();
if($request->get('category')){
$products->whereHas('category',function($q) use($request){
$q->where('category_id',$request->get('category'));
});
}
$products = $query->get();
But I can't do this I get newQuery() method is not available.
Is there a better way of performing optional queries based on parameters?
Change your code to this to make it work:
$products = Product::where('user_id', auth()->id());
if (request('category')) {
$products = $products->whereHas('category', function($q) {
$q->where('category_id', request('category'));
});
}
$products = $products->get();
Alternatively, you could just load related products using lazy eager loading:
auth()->user()->load(['products' => function($q) {
if (request('category')) {
$q->whereHas('category', function($q) {
$q->where('category_id', request('category'));
});
}
}]);
Just for a bit of neatness you can use the when() method on the builder class too
auth()->user()->products()
->when($request->category, function ($query) use ($request) {
$query->whereCategoryId($request->category);
})->get();
or
Product::whereUserId(auth()->id())
->when($request->category, function ($query) use ($request) {
$query->whereCategoryId($request->category);
})->get();
I getting results from a query built using Eloquent (Laravel's ORM)
$query = Lawyer::whereHas('user', function($q) use ($request) {
$q->where('is_active', true);
});
$result = $query->get()
I would like to pass the results I get throught a trasformer class LawyerTransformer extends TransformerAbstract{} to add some data to the results.
When I try this :
$this->collection($query->get(), new LawyerTransformer())
I have the following issue : Method [collection] does not exist.
How can I transform all the results using a transformer ?
You could use the transform method on the collection instance to achieve something like that here is an example that will increment all values in an array by 1;
$collection = collect([1, 2, 3]);
$collection->transform(function ($item, $key) {
return (new IncrementTransformer)->transform($item);
});
And the transfomer class
class IncrementTransformer
{
public function transform($item)
{
return $item += 1;
}
}
You could probably write this a little cleaner but you get the basic idea.