The following....
Controller:
public function treatmentsList()
{
$treatments = Treatment::with('category')->where('status', 1)->orderBy('medical_name')->get();
$groups = $treatments->groupBy('category.name');
return view('pages.treatments.list', compact( 'groups'));
}
View:
<ul>
#foreach($groups as $category_name => $treatments)
<li>
<h4>{{ $category_name }}</h4>
<ul>
#foreach($treatments as $treatment)
<li>
<h5>{{ $treatment->medical_name }}</h5>
</li>
#endforeach
</ul>
</li>
#endforeach
</ul>
Gives me...
The treatments grouped in their categories and in alphabetical order but the categories are not in alphabetical order.
- Category B
Treatment A
Treatment B
Treatment C
- Category A
Treatment A
Treatment B
- Category C
Treatment A
Treatment B
Treatment C
Treatment D
How can I get the treatments and categories both listed in alphabetical order?
You can get this by adding query constraints for the eager loading query:
Treatment::with(['category' => function ($query) {
$query->orderBy('name');
}])
->where('status', 1)
->orderBy('medical_name')
->get();
Hi,
I'm not sure if this is what you mean... but it did not worked... I still get the categories not alphabetically listed.
public function treatmentsList()
{
$treatments = Treatment::with(['category' => function ($query) {
$query->orderBy('name');
}])
->where('status', 1)
->orderBy('medical_name')
->get();
$groups = $treatments->groupBy('category.name');
return view('pages.treatments.listA', compact( 'groups'));
}
You're right - my mistake. I think that answer is the join query:
public function treatmentsList()
{
$treatments = Treatment::with('category')
->select('treatment.*')
->join('category', 'category.id', '=', 'treatment.category_id')
->where('treatment.status', 1)
->orderBy('category.name')
->orderBy('treatment.medical_name')
->get();
$groups = $treatments->groupBy('category.name');
return view('pages.treatments.listA', compact( 'groups'));
}
Check the names of tables and fields from above example. This should work.
Related
I need your help...
Can't figure out where the problem is.
I am trying to show all products of a subcategory.Sometimes it shows the first or the last record. Then it repeats many times the same record( as the cycle is).
category: id, name, visible
products:id, name,
category_products:id, id_product, id_category
Route::get('navigation/{id}',function($id){
$prods= \App\Products_to_Categories::where('id_category',$id)->get();
$products=array();
foreach ($prods as $prod)
{
$products[] = \App\Products::find($prod->id_product)->
where('visible','yes')
-> where('delete','no')->first();
}
return view('subcategories.order_products',
['products'=>$products ]);}
View blade
<div class="col-md-6 col-md-offset-1">
<ul id="sortable">
#foreach($products as $product)
<li class="ui-state-default" id="{{ $product->id}}"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span> {{$product->name}}</li>
#endforeach
</ul>
</div>
It looks like products and categories are related through the join table category_products, so you can setup a belongsToMany() relationship and query from Category to Product without looping over the join table.
https://laravel.com/docs/5.7/eloquent-relationships#many-to-many
Category model:
public function products()
{
return $this->belongsToMany(\App\Products::class, 'category_products', 'id_category', 'id_product');
}
Products model:
public function categories()
{
return $this->belongsToMany(\App\Category::class, 'category_products', 'id_product', 'id_category');
}
Controller code:
$category = Category::find($id);
$products = $category->products()
->where('visible', 'yes')
->where('delete', 'no')
// ->inRandomOrder() // un-comment this if you want results in random order
->get();
Try this in Laravel >= 5.2: :
$prods= \App\Products_to_Categories::where('id_category',$id)->get();
$products=array();
$whereIn = array();
foreach ($prods as $prod)
{
$whereIn[] = $prod->id_product;
}
$products[] = \App\Products::find($prod->id_product)
->where('visible','yes')
-> where('delete','no')
->whereIn('id', $whereIn)
->orderByRaw('RAND()')
->get();
This will give you the list of products of a specific category in random order.
Laravel 4.2.7 - 5.1:
User::orderByRaw("RAND()")->get();
Laravel 4.0 - 4.2.6:
User::orderBy(DB::raw('RAND()'))->get();
Laravel 3:
User::order_by(DB::raw('RAND()'))->get();
source :
Laravel - Eloquent or Fluent random row
I'm using laravel 5.6.
I have 3 tables : players, games and game_player table.
Retrieving the game count of every player is easy with this in the index action of the PlayersController:
//Get game count of every player
$players = Player::withCount('games')->get();
Is there a way to retrieve the game count for the games when the player has won the game? (in the index action of the playerscontroller) I'm not sure how to do this. Can someone help?
games table migration
$table->integer('winner')->unsigned()->index();
$table->foreign('winner')->references('id')->on('players')->onDelete('cascade');
game_player table migration
table->integer('game_id')->unsigned()->nullable();
$table->foreign('game_id')->references('id')->on('games')->onDelete('cascade');
$table->integer('player_id')->unsigned()->nullable();
$table->foreign('player_id')->references('id')->on('players')->onDelete('cascade');
game model relation
public function players(){
return $this->belongsToMany('App\Player')->withTimestamps();
}
//this is for the winner of the game
public function player()
{
return $this->hasOne('App\Player');
}
player model relation
public function games(){
return $this->belongsToMany('App\Game')->withTimestamps();
}
//the game the player has won
public function game()
{
return $this->belongsTo('App\Game');
}
playerscontroller
public function index()
{
$players = Player::all();
//Get game count of every player
$players = Player::withCount('games')->get();
/*Load the view and pass the groups*/
return \View::make('players.index')->with('players', $players);
}
The result I want is to get played games (is working) and won games.
player > index blade
#foreach($players as $player)
<p>{{ $player->id }}</p>
<p>{{ $player->firstname }} {{ $player->lastname }}</p>
<ul>
<li>Played games: {{ $player->games_count }}</li>
<li>Won games: </li>
</ul>
#endforeach
update
I don't think we can see it as a duplicate of this question (Laravel using where clause on a withCount method) because I'm using a many to many relationship also.
If I use this code which isn't the correct one, because the 1 needs to be dynamic $id:
$players = Player::withCount('games')
->having('winner', '=', 1)
->get();
I get the error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'winner' in 'having clause' (SQL: select players., (select count() from games inner join game_player on games.id = game_player.game_id where players.id = game_player.player_id) as games_count from players having winner = 1)
update 2
When I use this code:
controller
$players = Player::all();
//Get game count of every player
$players = Player::withCount('games')->get();
$wongames = Player::withCount(['games' => function($query) { $query->where('winner', '=', 5); }])->get();
//$players = Player::withCount('games')->where('winner', '=', 1);
/*Load the view and pass the groups*/
return \View::make('players.index')->with('players', $players)->with('wongames', $wongames);
blade index
#foreach($players as $player)
<p>{{ $player->id }}</p>
<p>{{ $player->firstname }} {{ $player->lastname }}</p>
<ul>
<li>Played games: {{ $player->games_count }}</li>
#foreach($wongames as $wongame)
<li>Won games: {{ $wongame->games_count }}</li>
#endforeach
</ul>
#endforeach
I get this (not what I exactly want, but getting there I think):
Since you define a foreign key on the games table, you have a one-to-many relationship between the Player and Game already. Try adding the following relation to your Player model:
// Player.php
public function won()
{
// must specify the foreign key because it is not the usual `_id` convention.
return $this->hasMany(Game::class, 'winner');
}
Then access it on each player like:
#foreach($players as $player)
{{ $player->won->count() }}
#endforeach
Rather than querying in the view file, you should ideally do the following in your controller:
public function index()
{
/*Load the view and pass the groups*/
return \View::make('players.index')->with('players', Player::with('won')->get());
}
How can I hide empty categories when I query to display them? Empty categories are those that no products are assigned to them..
Here is my controller
public function showSubCats($categoryId) {
$subcats = SubCategories::where('category_id', '=', $categoryId)->get();
return View::make('site.subcategory', [
'subcats' => $subcats
]);
}
Here is the view
#if(count($subcats) > 0)
<div class="row">
#foreach($subcats as $i => $subcategory)
// display categories
#endforeach
#else
There is no products assigned to this category
</div>
#endif
This is my SubCategories model
public function products()
{
return $this->hasMany('Product', 'category_id');
}
public function subcategories()
{
return $this->hasMany('SubCategories', 'category_id');
}
public function lowestProduct() {
return $this->products()->selectRaw('*, max(price) as aggregate')
->groupBy('products.product_id')->orderBy('aggregate');
}
In product table I have column which is sub_cat_id and holds category in which is assigned. If is 0 is not assigned to any category.
How can I hide empty categories now?
You should use where in addition to your model
return $this->hasMany('Action')->where('sub_cat_id', 1);
Note :
I believe that you neeed to take the records only that has sub_cat_id as 1. If not change it to 0 or accordingly.
Hope this helps you
There are two table category and content.
Content table has id,category_id
Category table has category name and id
I have to fetch all the content from content table with category name which is in Category Table.
I don't want to use JOIN.
Please suggest me other query to use.
Controller
$AllContent=Content::all();
return View::make('ALL/Contents')->with('AllContent',$AllContent);
I have no idea why you don't want to use an SQL JOIN, as it is almost certainly the most efficient thing to do when you have one table relating to another by its primary key. But hey, who am I to judge?
A way you can do this is by using a subquery. In plain old SQL:
SELECT
`content`.`id`,
(
SELECT `name`
FROM `category`
WHERE `id` = `content`.`category_id`
) AS `name`
FROM `content`
So, to do this using Laravel's query builder:
DB::table('content')
->select(['id'])
->selectSub(function ($query) {
return $query->from('category')
->where('id', '=', DB::raw('content.category_id'))
->select('name')
}, 'name')
->get();
I think that'll do what you want, anyway. But really, just use a JOIN.
If you do want to use a JOIN then you're somewhat better off. Again I'll give you the raw SQL first:
SELECT
`content`.`id`,
`category`.`name`
FROM `content`
LEFT JOIN `category`
ON `content`.`category_id` = `category`.`id`
And now in Laravel's query builder:
DB::table('content')
->join('category', 'content.category_id', '=', 'category.id')
->select(['content.id', 'category.name'])
->get();
And now using Eloquent models:
// app/Models/Content.php
namespace App\Models;
class Content
{
protected $table = 'content';
public function category()
{
return $this->belongsTo('App\Models\Category', 'category_id');
}
}
// app/Models/category.php
namespace App\Models;
class Category
{
protected $table = 'category';
public function content()
{
return $this->hasMany('App\Models\Content', 'category_id');
}
}
Now you can use this model to do all sorts. For example, to do what you were originally trying:
// some controller somewhere
use App\Models\Content;
class SomeController
{
public function index()
{
$content = Content::with('category')->get();
return View::make('ALL/Contents')->with('AllContent', $content);
}
}
// views/ALL/Contents.blade.php
<ul>
#foreach ($AllContent as $content)
<li>
{{ $content->id }} - {{ $content->category->name }}
</li>
#endforeach
</ul>
But alternatively you can return the content id/category name as a key=>value array:
// in the controller
$content = Content::with('category')->lists('category.name', 'id');
// in the view
<ul>
#foreach ($AllContent as $id => $category)
<li>
{{ $id }} - {{ $category }}
</li>
#endforeach
</ul>
This is all untested, but should work. If it doesn't, it should at least give you an idea of how it all goes together.
I am using Laravel 5.1 and I need to limit the number of related records I am pulling using a polymorphic many-to-many relationship.
What I would like to do is get a list of categories by parent_id. For each category then I'd like to only pull four posts.
I have have this working with the code below, but it results in a bunch of extra queries. I'd like to just hit the database once if possible. I'd like to use the Laravel/Eloquent framework if at all possible, but am open to whatever works at this point.
#foreach ($categories as $category)
#if ($category->posts->count() > 0)
<h2>{{ $category->name }}</h2>
See more
{-- This part is the wonky part --}
#foreach ($category->posts()->take(4)->get() as $post)
{{ $post->body }}
#endforeach
#endif
#endforeach
PostsController
public function index(Category $category)
{
$categories = $category->with('posts')
->whereParentId(2)
->get();
return view('posts.index')->with(compact('categories'));
}
Database
posts
id - integer
body - string
categories
id - integer
name - string
parent_id - integer
categorizables
category_id - integer
categorizable_id - integer
categorizable_type - string
Post Model
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function categories()
{
return $this->morphToMany('App\Category', 'categorizable');
}
Category Model
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
public function category()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function subcategories()
{
return $this->hasMany('App\Category', 'parent_id')->orderBy('order', 'asc');
}
public function posts()
{
return $this->morphedByMany('App\Post', 'categorizable');
}
I have seen a number of links to this on the web, but nothing that has actually worked for me.
I have tried this solution without any luck.
$categories = $category->with('posts')
->whereParentId(2)
->posts()
->take(4)
->get();
I have looked into this solution by Jarek at SoftOnTheSofa, but it is for a hasMany relationship and to be honest is a bit beyond my sql skill for me to adapt it for my needs.
Edit
I added a github repo for this setup, if it is useful to anybody.
Edit your Category model
public function fourPosts() {
// This is your relation object
return $this->morphedByMany('App\Post', 'categorizable')
// We will join the same instance of it and will add a temporary incrementing
// Number to each post
->leftJoin(\DB::raw('(' . $this->morphedByMany('App\Post', 'categorizable')->select(\DB::raw('*,#post_rank := IF(#current_category = category_id, #post_rank + 1, 1) AS post_rank, #current_category := category_id'))
->whereRaw("categorizable_type = 'App\\\\Post'")
->orderBy('category_id', 'ASC')->toSql() . ') as joined'), function($query) {
$query->on('posts.id', '=', 'joined.id');
// And at the end we will get only posts with post rank of 4 or below
})->where('post_rank', '<=', 4);
}
Then in your controller all categories you get with this
$categories = Category::whereParentId(1)->with('fourPosts')->get();
Will have only four posts. To test this do (remember that now you will load your posts with fourPosts method, so you have to access the four posts with this property):
foreach ($categories as $category) {
echo 'Category ' . $category->id . ' has ' . count($category->fourPosts) . ' posts<br/>';
}
In short you add a subquery to the morph object that allows us to assign temporary incrementing number for each post in category. Then you can just get the rows that have this temporary number less or equal to 4 :)
Does this work for you?:
$categories = $category->with(['posts' => function($query)
{
$query->take(4);
})
->whereParentId(2)
->get();
I think the most cross-DBMS way to do this would be using union all. Maybe something like this:
public function index(Category $category)
{
$categories = $category->whereParentId(2)->get();
$query = null;
foreach($categories as $category) {
$subquery = Post::select('*', DB::raw("$category->id as category_id"))
->whereHas('categories', function($q) use ($category) {
$q->where('id', $category->id);
})->take(4);
if (!$query) {
$query = $subquery;
continue;
}
$query->unionAll($subquery->getQuery());
}
$posts = $query->get()->groupBy('category_id');
foreach ($categories as $category) {
$categoryPosts = isset($posts[$category->id]) ? $posts[$category->id] : collect([]);
$category->setRelation('posts', $categoryPosts);
}
return view('posts.index')->with(compact('categories'));
}
And then you'd be able to loop through the categories and their posts in the view. Not necessarily the nicest looking solution but it would cut it down to 2 queries. Cutting it down to 1 query would probably require using window functions (row_number(), in particular), which MySQL doesn't support without some tricks to emulate it (More on that here.). I'd be glad to be proven wrong, though.
Here's a "half answer". The half I give you is to show you the MySQL code. The half I can't give you is translating it into Laravel.
Here's an example of finding the 3 most populous cities in each province of Canada:
SELECT
province, n, city, population
FROM
( SELECT #prev := '', #n := 0 ) init
JOIN
( SELECT #n := if(province != #prev, 1, #n + 1) AS n,
#prev := province,
province, city, population
FROM Canada
ORDER BY
province ASC,
population DESC
) x
WHERE n <= 3
ORDER BY province, n;
More details and explanation are found in my blog on groupwise-max.
I think the best option (without using raw queries) would be to setup a custom method to be used with the eager loading.
So in the Category model you could do something like:
<?php
public function my_custom_method()
{
return $this->morphedByMany('App\Post', 'categorizable')->take(4);
}
And then use that method when eager loading:
<?php
App\Category::with('my_custom_method')->get();
Make sure to use my_custom_method() instead of posts() in your view:
$category->my_custom_method()
You can try this one, that work for me!
$projects = Project::with(['todo'])->orderBy('id', 'asc')->get()->map(function($sql) {
return $sql->setRelation('todo', $sql->todo->take(4));
});