For an app, I'm creating a scraper.
At the moment I'm at the page where a user can add items to a list with the possibility to create any subcategories where you can add append those items to a specific subcategory.
At the bottom of the page, there is a section where all the subcategories are listed and below each category should be a list where all the items are who are appended to that specific subcategory.
At the moment, I was able to show all the names of the subcategories and all products that are in my main list are stored in the subcategories that has the id. (I hope you can follow what's going on). But I don't know how I can only show the articles who was that specific subcategory in that list instead of all of the main articles..
My code:
My view:
<div class="bg-gray-100 shadow-xl w-11/12 m-auto p-4 rounded-md space-y-6 relative">
<strong>Mijn sublijsten</strong>
#foreach ($subcats as $scat)
<div class="flex lg:flex-row">
<h2>{{ $scat->name }}</h2>
#if ($wishlists->contains('subcat', $cat->id))
#foreach ($wishlists as $item)
<strong>{{$item->article->title}}</strong>
#endforeach
#endif
</div>
#endforeach
</div>
My Controller
public function dashboard($role, $id)
{
$wishlists = Wishlist::all();
$articles = Article::all();
$websites = Website::all();
$categories = Category::all();
$subcats = Subcat::all();
$subcat = Wishlist::find($id)->get();
return view('dashboard', compact('wishlists', 'articles', 'categories', 'websites', 'subcats', 'subcat'));
}
My Model
class Wishlist extends Model
{
protected $table = "wishlists";
protected $fillable = [
'user_id',
'article_id',
'confirmed',
'available',
'score',
'subcat'
];
protected $casts = [
];
public function user()
{
return $this->belongsTo(User::class);
}
public function article()
{
return $this->belongsTo(Article::class);
}
public function subcat()
{
return $this->belongsTo(Subcat::class);
}
}
Related
model video
protected $table = 'lokit_video';
protected $fillable =
[
'title',
'cover_img',
'trailer',
'url',
'order_',
'active',
'description',
'lokit_category_id',
'duration'
];
public function lokit_category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
model category
protected $table = 'lokit_category';
protected $fillable = ['name'];
in controller
public function index(){
$dataCategory = Category::all();
$dataVideo = Video::all();
$video = Video::where('lokit_category_id', $dataCategory)->get();
dd($video);
return View('bnpt.content.home',compact('dataCategory','dataVideo'));
}
when I try the code above what happens with the code is null, how to fix it?
If you want to get all the videos related to all the categories, you should develop the relationship.
In Category Model:
public function videos()
{
return $this->hasMany(Video::class);
}
In the controller method:
$dataCategory = Category::all();
return View('bnpt.content.home',compact('dataCategory'));
In the view file:
<ul>
#foreach($dataCategory as $category)
<li><span>{{$category->name}}</span>
<ul>
#foreach($category->videos as $video)
<li><span>{{$video->name}}</span></li>
#endforeach
</ul>
</li>
#endforeach
</ul>
[PROBLEM]In your code you have
$dataCategory = Category::all();
$video = Video::where('lokit_category_id', $dataCategory)->get();
$dataCategory is a collection of the model instances of all the entries in your lokit_category table.
[SOLUTION]
You can't compare a collection with a field that expects an id in your case in the where statement.
lokit_category_id should be compared with the id of a category.
If you have the lokit_category_id
$videos = Video::where('lokit_category_id', $lokit_category_id)->get();
dd($videos);
If you have the name of the lokit_category, get the id using that name and then make the query.
// Get the category for which you want to get all the videos.
$categoryName = 'CATGEORY NAME';
$category = Category::where('name', $categoryName)->first();
$videos = Video::where('lokit_category_id', $category->id)->get();
dd($videos);
[IF YOU WANT VIDEOS WITH RESPECT TO A CATEGORY]
In your Category Model
public function videos(){
return $this->hasMany(App\Video::class, 'lokit_catgeory_id', 'id');
}
Controller
Use eager loading as it will help in reducing the number of queries.
$categories = Category::with('videos')->get();
foreach($categories as $category){
// You can access the videos for the category using the videos relation.
$category->videos;
}
I want to retrieve products of the selected category, as well as sub-categories and I may even need to order them according to price or date. Please let me know what changes should be made to the controller!
I am using a Many to Many Relationship for the 2 tables: Categories and Products, and a category_product to relate both.
Example
(Not all categories have sub-categories)
Gifts
Books
Toys
Boys
Girls
Phone
Samsung
Nokia
If a user clicks Phone, all products of the category 'Phone', 'Samsung' or 'Nokia' should appear!
Database
Products: id, name, price, created_at
Categories: id, name, slug, parent_id, sorting_order, created_at
category_product: id, category_id, product_id
Code:
Category Model:
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
}
Product Model
class Product extends Model
{
public function categories()
{
return $this->belongsToMany('App\Category');
}
}
class ProductController extends Controller {
public function index($slug, Request $request)
{
if ( ! isset($_GET['sortBy']))
{
$category = Category::where('slug', $slug)->with('products')->first();
if ( ! $category)
{
abort(404, 'Page not found');
}
}
else
{
$slug = $request->segments()[1];
$products = Category::where('slug', $slug);
switch ($request->sortBy)
{
case 'latest':
$category = $products->with(['products' => function ($q) {
$q->orderBy('created_at', 'desc');
}])->first();
break;
case 'asc':
$category = $products->with(['products' => function ($q) {
$q->orderBy('price', 'asc');
}])->first();
break;
case 'desc':
$category = $products->with(['products' => function ($q) {
$q->orderBy('price', 'desc');
}])->first();
break;
default:
$category = $products->with('products')->first();
break;
}
}
return view('products', compact('category'));
}
}
View
<form id="sortProducts" class="form-inline" method="get">
{{ csrf_field() }}
<label for="sortBy">Sort By:</label>
<select name="sortBy" id="sortBy">
<option value="latest">Latest</option>
<option value="asc">Price Low to Hight</option>
<option value="desc">Price High to Low</option>
</select>
</form>
#foreach($category->products as $product)
<div class="product">
<img border="0" src="{{Voyager::image($product->image)}}" alt="{{$product->name}}">
<div class="product-name">{{$product->name}}</div>
<div class="product-price">${{$product->price}}</div>
</div>
#endforeach
I am using Laravel Version 6.2 along with Voyager Version 1.3.
If your category depth is unlimited, you'll need a recursive relationship.
I think something like this could work:
In your Category Model:
public function nestedStructure()
{
return $this->children()->with([
'nestedStructure',
'products' => function($q) {
$q->orderBy('created_at', 'desc');
}
);
}
Okay so first you need to modify your Category Model to get all children along with parent
Category Model
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
// recursive, loads all descendants
public function recursiveChildren()
{
return $this->children()->with('recursiveChildren');
}
}
Now according to given many-to-many relation a single Product might belong to multiple categories but we wont like to have same product show up again and again. So a possible fix for your Controller can be
class ProductController extends Controller {
public function index($slug, Request $request)
{
$categories = Category::where('slug', $slug)->with('recursiveChildren')->whereNull('parent')->get();
if($categories->count() == 0) abort(404, 'Page not found');
$products = collect([]); // its a helper method to make collections
foreach($categories as $category) {
$category_products = $category->products;
foreach($category_products as $product) {
if(!$products->contains($product)) $products->push($product);
}
}
if($request->input('soryBy')) {
switch ($request->sortBy)
{
case 'latest':
$products = $products->sortBy('created_at');
break;
case 'asc':
$products = $products->sortBy('price');
break;
case 'desc':
$products = $products->sortByDesc('price');
break;
default:
$products;
break;
}
}
return view('products', compact('products'));
}
}
Now lets modify the view a little
<form id="sortProducts" class="form-inline" method="get">
{{ csrf_field() }}
<label for="sortBy">Sort By:</label>
<select name="sortBy" id="sortBy">
<option value="latest">Latest</option>
<option value="asc">Price Low to Hight</option>
<option value="desc">Price High to Low</option>
</select>
</form>
#foreach($products as $product)
<div class="product">
<img border="0" src="{{Voyager::image($product->image)}}" alt="{{$product->name}}">
<div class="product-name">{{$product->name}}</div>
<div class="product-price">${{$product->price}}</div>
</div>
#endforeach
There are at least two solutions.
Solution 1 (Pure Laravel):
Add these two methods to your Category model:
public function descendants()
{
$collection = new \Illuminate\Support\Collection();
foreach ($this->children as $child) {
$collection->add($child);
$collection = $collection->merge($child->descendants());
}
return $collection;
}
public function getRouteKeyName()
{
return 'slug';
}
And use it in your ProductController controller like so:
class ProductController extends Controller
{
public function index(Request $request, Category $category)
{
$categories = $category->descendants()->add($category)->pluck('id');
$products = DB::table('category_product AS cp')
->join('products', 'cp.product_id', '=', 'products.id')
->select('products.*')
->whereIn('cp.category_id', $categories)
->get();
return view('products', compact('category', 'products'));
}
}
You can then output them in your view file:
#forelse($products as $product)
<div class="product">
<img border="0" src="{{ Voyager::image($product->image) }}" alt="{{ $product->name }}">
<div class="product-name">{{ $product->name }}</div>
<div class="product-price">${{ $product->price }}</div>
</div>
#empty
<div class="product">
There are no products in this category.
</div>
#endforelse
Solution 2 (Using a package):
First of all, install the package:
composer require kalnoy/nestedset
Replace parent_id column with $table->nestedSet(); in your categories table and related migration file:
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('slug')->unique();
$table->nestedSet();
$table->timestamps();
});
Then, update your Category model like so:
use Illuminate\Database\Eloquent\Model;
use Kalnoy\Nestedset\NodeTrait;
class Category extends Model
{
use NodeTrait;
protected $fillable = [
'name',
'slug',
];
public function products()
{
return $this->belongsToMany('App\Product');
}
public function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function getRouteKeyName()
{
return 'slug';
}
}
You can now use it in your controller:
class ProductController extends Controller
{
public function index(Request $request, Category $category)
{
$categories = Category::descendantsAndSelf($category->id)->pluck('id');
$products = DB::table('category_product AS cp')
->join('products', 'cp.product_id', '=', 'products.id')
->select('products.*')
->whereIn('cp.category_id', $categories)
->get();
return view('products', compact('category', 'products'));
}
}
You can output as shown in Solution 1.
Please note that I assumed you use {category} key in your route definition. (See Route Model Binding) For example:
Route::get('/products/category/{category}', 'ProductController#index');
Read the documentation first to create, update and delete an item in a nested set (categories).
I'm Trying to display restaurant Menus grouped by the categories, for example...
Lunch
Chicken and Chips
Rice
Breakfast
Tea
Coffee
So I have 3 tables in my database, Restaurants, Categories, And Menus
Category Model
class Category extends Model
{
protected $fillable = [
'name'
];
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function menus_type()
{
return $this->hasMany('App\Menu','category_id');
}
}
Menu Model
class Menu extends Model
{
protected $fillable = [
'name',
'price',
'description',
'photoPath',
'restaurant_id',
'category_id',
];
/**
* Menu belongs to Restaurant
*/
public function restaurant()
{
return $this->belongsTo('App\Restaurant');
}
/**
* Menu belongs to category type
*/
public function category_type()
{
return $this->belongsTo('App\Category', 'category_id');
}
}
Restaurants controller
public function show($slug, RestaurantRepository $repository){
if(! $restaurant = $repository->get(['slug' => $slug], with(['cuisines','user', 'photos', 'thumbs', 'region', 'ratings.user']))) return abort('404');
$menus=Menu::with(['category_type'])->where('restaurant_id',$restaurant->id)->get()->groupBy('category_id');
return view('frontend.restaurant.show', compact('restaurant', 'p','menus'));
}
when i Dump this it looks just fine.
Results have been grouped
Now my trouble is on the View when i try to get results of this i get an error.
#if($menus)
<ul>
#foreach($menus as $m)
<li>
{{$m->name}}
</li>
#endforeach
</ul>
#endif
ErrorException (E_ERROR).
Property [name] does not exist on this collection instance.
Iterate inner loop also. 'groupBy()' creates another array with category_id as key.
#if($menus)
<ul>
#foreach($menus as $category_id=>$outer)
#foreach($outer as $k=>$inner)
<li>
{{$category_id}}: {{$inner->name}}
</li>
#endforeach
#endforeach
</ul>
#endif
Updated you query to get from category
$categories = Category::with('menus_type')->get();
return view('frontend.restaurant.show', compact('restaurant', 'p','categories '));
In your view
#if($categories ?? false)
<ul>
#foreach($categories ?? [] as $cat_key=>$category)
<li>
{{$category->name}}
</li>
<li>
<ul>
#foreach($category->menus_type as $menu_key=>$menu)
<li>
{{$menu->name}}
</li>
#endforeach
</ul>
</li>
#endforeach
</ul>
#endif
I need to insert sub categories into my main categories. I have already done all the work for Showing, Adding, Editing, and Deleting parent Categories. But now im stuck with how to actually add a sub category to one of my parents category.
Here is how the table looks like for the category's and sub categories.
As you can see I already Have a sub category under iPhone's, which I added manually through the database. To add a sub category to a main category, I just click on the + sub-Category link which takes me to the form to add sub category.
Here i my route to show and add a sub category:
Route::group(["middleware" => 'admin'], function(){
/** More category routes here -->, just hidden for shortness **/
/** Show the Admin Add Sub-Categories Page **/
Route::get('admin/categories/addsub/{id}', [
'uses' => '\App\Http\Controllers\CategoriesController#addSubCategories',
'as' => 'admin.category.addsub',
'middleware' => ['auth'],
]);
/** Post the Sub-Category Route **/
Route::post('admin/categories/postsub/{id}', [
'uses' => '\App\Http\Controllers\CategoriesController#addPostSubCategories',
'as' => 'admin.category.postsub',
'middleware' => ['auth'],
]);
});
Here is my CategoriesController.php:
It is shortened just to show sub-categories functions. This is where I'm having trouble adding a sub category to a parent category
class CategoriesController extends Controller
/**
* Return the view for add new sub category
*
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function addSubCategories($id) {
$category = Category::findOrFail($id);
return view('admin.category.addsub', compact('category'));
}
/**
* #param $id
* #param CategoryRequest $request
* #return \Illuminate\Http\RedirectResponse
*/
public function addPostSubCategories($id, CategoryRequest $request) {
// Find the Parent Category ID
$category = Category::findOrFail($id);
// Insert into categories where the Parent_id = to the category ID
$categories = Category::where('parent_id', '=', $category);
// Assign $category to the Category Model, and request all validation rules
$categories = new Category($request->all());
// Then save the newly created category in DB
$categories->save();
// Flash a success message
flash()->success('Success', 'Sub Category added successfully!');
// Redirect back to Show all categories page.
return redirect()->route('admin.category.show');
}
}
My Category.php Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $table = 'categories';
protected $fillable = ['name'];
protected $guarded = ['id'];
public function parent() {
return $this->belongsTo('App\Category', 'parent_id');
}
public function children() {
return $this->hasMany('App\Category', 'parent_id');
}
}
My add sub category form:
<form role="form" method="POST" action="{{ route('admin.category.postsub', $category->id) }}">
{{ csrf_field() }}
<li class="collection-item blue">
<h5 class="white-text text-center">
Sub-Category to {{ $category->name }}
</h5>
</li>
<li class="collection-item">
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<input type="text" class="form-control" name="name" value="{{ old('name') }}" placeholder="Add Sub-Category">
#if($errors->has('name'))
<span class="help-block">{{ $errors->first('name') }}</span>
#endif
</div>
</li>
<li class="collection-item blue">
<div class="form-group text-center">
<button type="submit" class="btn btn-link grey lighten-5">Create Sub-Category</button>
</div>
</li>
</form>
And my Database structure:
I particulary need help in my addPostSubCategories() function in my CategoriesController, because right now if I add a new SUB category, it just adds a new Parent Category, not a sub category
Visit here to see a detailed explanation: https://laravel.com/docs/5.2/eloquent-relationships#inserting-related-models
Here's what you want:
/**
* #param $id
* #param CategoryRequest $request
* #return \Illuminate\Http\RedirectResponse
*/
public function addPostSubCategories($id, CategoryRequest $request) {
// Find the Parent Category
$category = Category::findOrFail($id);
// Create the new Subcategory
$subcategory = new Category($request->all());
// Save the new subcategory into the relationship
$category->children()->save($subcategory);
// Flash a success message
flash()->success('Success', 'Sub Category added successfully!');
// Redirect back to Show all categories page.
return redirect()->route('admin.category.show');
}
Hi I'm trying to output a certain column from my pivot table. To show you what I have tried, I will first show you my models (my pivot tables are ordertasks and tagtasks):
Task table:
class Task extends \Eloquent {
// Add your validation rules here
public static $rules = [
// 'title' => 'required'
];
// Don't forget to fill this array
protected $fillable = ['task_name','task_description','hour','difficulty'];
public function ordertask()
{
//oneToMany
return $this->hasMany('Ordertask', 'id_task', 'id');
}
public function tagtask()
{
//oneToMany
return $this->hasMany('Tagtask', 'id_task', 'id');
}
}
Tagtask table:
<?php
class Tagtask extends \Eloquent {
protected $fillable = ['id_tag','id_task'];
public function task()
{
//manyToOne
return $this->belongsTo('Task', 'id_task', 'id');
//return $this->belongsTo('Task');
}
public function tag()
{
//manyToOne
return $this->belongsTo('Tag', 'id_tag', 'id');
}
}
Ordertask table:
<?php
class Ordertask extends \Eloquent {
// Add your validation rules here
public static $rules = [
// 'title' => 'required'
];
// Don't forget to fill this array
protected $fillable = ['id_order','id_task', 'hour', 'hourprice','id_user', 'createdBy'];
public function user()
{
//manyToOne
return $this->belongsTo('User', 'id_user', 'id');
//return $this->belongsTo('User');
}
public function task()
{
//manyToOne
return $this->belongsTo('Task', 'id_task', 'id');
//return $this->belongsTo('Task');
}
}
Here is my TaskController.php with the following piece of code:
public function index()
{
$Tasks=Task::with('tagtask','ordertask')->get();
return View::make('user.tasks.index', compact('Tasks'));
}
Okay now comes the part where I want to show $Task->ordertask['id_user'] on my browser with the following piece of code:
#if (count($Tasks))
<ul>
#foreach($Tasks as $Task)
<div class="row">
<div class="col-md-3">
<li>
{{--as a third parameter we need to pass in the id of task, because it needs to be fed to
our actual user.task.edit route. --}}
{{link_to_route('user.tasks.edit', $Task['task_name'], array($Task->id))}}
</li>
</div>
<div class="col-md-3">
<li>
{{$Task->ordertask['id_user']}}
</li>
</div>
<div class="col-md-3">
<li>
{{$Task['task_hour']}}
</li>
</div>
<div class="col-md-3">
<li>
{{Form::open(array('route'=>array('user.tasks.destroy',$Task->id),
'method'=>'delete','class'=>'destroy'))}}
{{Form::submit('Delete')}}
{{Form::close()}}
</li>
</div>
</div>
#endforeach
</ul>
#endif
Unfortunately that doesn't work because I get the following error:
Undefined index: id_user
Instead of:
{{$Task->ordertask['id_user']}}
I have also tried this just to see what output it gave me:
{{$Task->ordertask}}
Which gave me the following output:
[{"id":5,"id_order":2,"id_task":1,"hour":63,"hourprice":15,"id_user":5,"createdBy":4,"created_at":"2014-10-13 10:21:33","updated_at":"2014-10-13 10:21:33"}]
So gladly I want to output id_user from the ordertask table. Gladly I'm waiting on your answer. Anyway thanks for your response.
One Task can have many order tasks, so to display users instead of
{{$Task->ordertask['id_user']}}
You should use:
#foreach ($Task->ordertask as $ot)
{{ $ot->id_user }}
#endforeach