Issue with Laravel orderby - php

I am trying to order my products by price once the client click order by inside the product page. I keep getting the next error: Undefined index: title (View: C:\xampp\htdocs\eshop\resources\views\content\item.blade.php) .
My 'item.blade.php' is the page that shows the larger size of the product in separate page. I think the issue is inside my controller Item function or im my model getItem function , I might be wrong... would appreciate if you could help me out .Thanks
My Route :
Route::get('shop/{category_url}/sorting-{sort?}', 'ShopController#products');
My view in content.products:
#if($products)
<br><br>
High to low |
Low to high
My item.blade.php:
#extends ('master')
#section('content')
<div class="row">
<div class="col-md-12 text-center">
#if('item')
<h1>{{ $item['title']}}</h1>
<p><img width="500" src="{{ asset ('images/' . $item['image'])}}" </p>
<p>{!! $item['article'] !!}</p>
<p><b>Price on site:</b>{{ $item['price']}}$</p>
<p>
#if(Cart::get($item['id']))
<input disabled="disabled" type="button" value="In Cart!" class="btn btn-success">
#else
<input data-id="{{ $item['id']}}" type="button" value="+ Add to cart" class="btn btn-success add-to-cart">
#endif
Checkout
</p>
#else
<p class="text-center" style="font-size: 18px">No product details ...</p>
#endif
</p>
#endsection
My Controller:
public function products(Request $request, $category_url, $sort= 'ASC'){
Product::getProducts($category_url, self:: $data);
$catsort = Categorie::where('url', '=', $category_url)->first();
$products = Product::where('categorie_id', $catsort->id)->orderBy('price', $sort)->get();
return view('content.products', self::$data ,['products' => $products, 'sort' => $sort]);
}
public function item($category_url, $product_url){
Product::getItem($product_url, self::$data);
return view('content.item', self::$data);
}
My Model:
static public function getProducts($category_url, &$data){
$data['products']=$data['category']=[];
if ($category=Categorie::where('url','=', $category_url)->first()){
$category= $category->toArray();
$data['category']=$category;
$data['title']=$data['title']. ' | ' . $category['title'];
if ($products=Categorie::find( $category['id'])->products){
$data['products']= $products->toArray();
}
}
}
static public function getItem($product_url, &$data) {
$data['item'] = [];
if ($product = Product::where('url', '=', $product_url)->first()) {
$product = $product->toArray();
$data['item'] = $product;
$data['title'] .= '|' . $product['title'];
}
}

So based on the chat, I'm going to try and provide an answer.
To start, we need to understand how you want to structure your database.
I can see for sure you have Products & Categories. I'm still not clear what you're trying to achieve with the $items.
So, starting with this, you have two tables. Lets think this through to determine what relationship they have. To start, ask the question, how many categories can a product have (1 or more than 1)? Most people structure categories so products can have more than one, so in Laravel, this is known as a HasMany relationship. The inverse of this is "how many products can a category have?". The answer, once again in this case, is more than 1. Therefore, instead of a HasMany relationship, this is actually a BelongsToMany relationship. A category can have many products, and a product can have many categories.
Next, you need to create a pivot table. This is a table that will store the ID of the product, and the ID of the category that have a relationship. You might create some migrations that look like this. Each of them should go in it's own migration file in the "up" method.
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug', 100)->index();
$table->integer('price');
$table->timestamps();
});
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug', 100)->index();
$table->timestamps();
});
Schema::create('category_product', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->index();
$table->integer('product_id')->index();
$table->timestamps();
});
Next we need to setup the models to accept the relationship. So, in your Product.php class, add the following method.
public function categories(){
return $this->belongsToMany(Category::class);
}
And the inverse of this in the Category.php class.
public function products(){
return $this->belongsToMany(Product::class);
}
Now, you should be able to access your relationships like so
$category = Category::first();
$products = $category->products;
Okay, now lets get this working. Your route looks fine, though I wouldn't add the sort the way you are. You can send this in as GET data like so http://example.com/shop/test-category?sort=ASC
Route::get('shop/{category_url}', 'ShopController#products');
Okay, so now your controller.
public function products(Request $request, $category_url){
$category = Category::with(['products' => function($q){
$q->orderBy('price', request()->get('sort')??"ASC");
}])->whereSlug($category_url)->first(); // Eager load in products
return view('content.products', ['category' => $category]);
}
Lastly, within your blade you can now use object syntax instead of array syntax.
#section('content')
Category Name: {{$category->name}} <br />
#foreach($category->products as $product)
Product Name: {{$product->name}} <br />
Product Price: {{$product->price}} <br />
Product Name: {{$product->name}} <br />
#endforeach
#endsection
Without seeing more, I can't be more specific as I'm not clear on how you're trying to handle your $items. You should set it up in the same way we setup products and categories though. Think about the relationship and determine if it's a "HasMany", "HasOne", or "BelongsToMany".
Also, there are default naming conventions you should try and follow. Your database tables should be lowercase and plural. So the table names should be exactly "products" and "categories". Your models should be singular. "Product" & "Category". No need to name it "Categorie". Then, for all pivot tables, you want to name them like the following "category_product". So singular and alphabetical order with the two tables you are pivoting on. Lastly, in your database, you'll want to make sure your pivot table columns are named "product_id" & "category_id", so the singular version of the database name + "_id".
I hope that's enough to get you going.

Related

Laravel 8: Base table or view not found

I have two tables at db, one of them is named users which simply contains user information of website and the other one is tags which contains some hashtags that users can choose from them.
I also created a table named tag_user that can store the tag_id and user_id like this image:
(just like Stackoverflow that a user can select multiple tags such as php, javascript & etc)
So in order to make this relationship between these two, I added this to User model:
public function tags()
{
return $this->belongsToMany(User::class);
}
And also this one to Tag model:
public function users()
{
return $this->belongsToMany(Tag::class);
}
And here is the select option on blade, and users can select multiple tags from db:
<select class="form-control BSinaBold" name="skills[]" id="skills" multiple>
#foreach(\App\Models\Tag::all() as $tag)
<option value="{{ $tag->id }}" {{ in_array($tag->id , Auth::user()->tags->pluck('id')->toArray()) ? 'selected' : '' }}>{{ $tag->name }}</option>
#endforeach
</select>
Now as soon as I load the blade, I got this as error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'dbname.user_user' doesn't exist (SQL: select users.*, user_user.user_id as pivot_user_id from users inner join user_user on users.id = user_user.user_id where user_user.user_id = 4)
So what's going wrong here? How can I fix this issue?
I would really appreciate any idea or suggestion from you guys...
And here is also the migration of tags & tag_user table:
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('label');
$table->timestamps();
});
Schema::create('tag_user', function (Blueprint $table) {
$table->unsignedBigInteger('tag_id');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->primary(['tag_id','user_id']);
});
}
Thanks in advance.
As #aynber described in the comments:
In User model
public function tags()
{
return $this->belongsToMany(Tag::class);
}
And in Tag model
public function users()
{
return $this->belongsToMany(User::class);
}

Laravel trying to get data from the pivot table

im trying to get the data linked to my model out of the pivot table (many to many relationship).
i put customers on a many to many relationship with departments.
the migration:
Schema::create('customer_department', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('customer_id');
$table->unsignedBigInteger('department_id');
$table->timestamps();
$table->foreign('customer_id')
->references('id')
->on('customers');
$table->foreign('department_id')
->references('id')
->on('departments');
});
the customer model:
public function department(){
return $this->belongsToMany('App\Department');
}
the department model:
public function customer(){
return $this->belongsToMany('App\Customer');
}
now im trying to print out every department the customer is assigned to in the view. i tried
{{$customer->department}}
or
#foreach($customer->department as $depo)
{{$depo->name}}
#endforeach
or
{{$customer->pivot->department_id}}
...
the controller:
public function show(Customer $customer)
{
return view('/customers/customer', ['customer' => $customer]);
}
however i get several error messages, empty arrays or straightup nothing. what am i doing wrong? what did i forget?
You have to have the model retrieved via the Many to Many relationship for it to have this attached pivot model. $customer would not have this pivot but everything returned from $customer->department would have these pivot models attached:
#foreach ($customer->department as $department)
{{ $department->pivot->department_id }}
#endforeach
Though, in this case you don't need to involve the pivot model since you want information from the Department models:
#foreach ($customer->department as $department)
{{ $department->getKey() }}
{{ $department->name }}
#endforeach
It would be a good idea to name these relationships in the plural since they return many, not in the singular.

How to use hasOne relationship correctly?

I am learning Laravel and I'm trying to create simple online store.
I created tables Items and Amounts. Now I want to display all Items with their amount in stock but for some reason unknown to me, amount of item is not fetched into items.
These are my schemas for tables:
Items:
Schema::create('items', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->string('name', 120)->nullable(false);
$table->float('price',8,2)->unsigned()->nullable(false);
$table->longText('short_specification');
$table->longText('specification');
$table->longText('description');
$table->string('photo', 100);
$table->engine = 'InnoDB';
$table->foreign('category_id')->references('id')->on('categories');
});
Amounts:
Schema::create('amounts', function (Blueprint $table) {
$table->integer('item_id')->unsigned();
$table->integer('amount')->unsigned()->nullable(false);
$table->engine = 'InnoDB';
});
Schema::table('amounts',function($table){
$table->foreign('item_id')->references('id')->on('items');
$table->primary('item_id');
});
These are my models:
Item:
class Item extends Model
{
public $timestamps = false;
function amount()
{
return $this->hasOne('App\Amount','item_id','id');
}
}
Amount:
class Amount extends Model
{
function item()
{
//$this->belongsTo('App\Item');
return $this->belongsTo('App\Item','item_id','id');
}
}
When I do:
$items = DB::table('items')->get();
dd($items);
return view('home')->with('items',$items);
Items are displayed correctly, but amount of item isn't there.
When I do:
#foreach($items as $item)
{{ $item->id }}
{{ $item->amount }}
#endforeach
I get:
Undefined property: stdClass::$amount (View: D:\2.
PROGRAMY\xampp\htdocs\silicon_store\resources\views\home.blade.php)
error.
From what I've seen on the web (I've been trying to fix this for over 3 hours now so I must be doing something totally wrong) it should work properly but it isn't.
With $items = DB::table('items')->get();, you're using the query builder. It won't have the value of the relationship unless you join the amounts table in the query.
$items = DB::table('items')
->leftJoin('amounts', 'items.id', '=', 'amounts.item_id')
->get();
I think you could also use an Eloquent query. In that case each $item would be an instance of the Item model rather than a StdClass object.
$items = App\Item::with('amount')->get();
or you can use kind of this query
$items = App\Item::whereHas('amount')->get()
Here link to understanding whereHas

Show only the latest category of product in Laravel 5.8

I have three models: products, category, product_category. A product may have many categories, and some categories may have a parent. You can find related relations below. I could show all categories with a foreach() loop, but I don't want to do that. I wish only to show the category, and it must be the latest. Is there a way to show only the last category of a parent category of the current product?
<div class="row">
#foreach ($products as $product)
#foreach ($product->categories as $cat)
{{ $cat->title }}
#endforeach
#endforeach
</div>
Product model
public function categories()
{
return $this->belongsToMany(Category::class);
}
Category model
public function products()
{
return $this->belongsToMany(Product::class);
}
public function subcategories()
{
return $this->hasMany('\App\Category', 'parent_id');
}
public function parent()
{
return $this->belongsTo('\App\Category', 'parent_id');
}
Categories schema
$table->increments('id');
$table->string('title');
$table->integer('parent_id')->nullable();
Category_product schema
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->integer('product_id')->unsigned();
You can use query builder on the relationship like so:
$product->categories->orderBy('id', 'desc')->first();
That will order from most recent to oldest and you can just take the first record.
You can simply use
$product->categories()->orderBy('id', 'DESC')->first();
Or you can also try
public function categories()
{
return $this->belongsToMany(Category::class)->orderBy('id','DESC');
}
Then you can use
$product->categories()->first();
Using specific methods on your model
Your categories definition would be something like this, right?
public function categories() {
return $this->belongsToMany(Category::class);
}
So, just create another method to retrieve only the latest.
public function latestCategory() {
return $this->categories()->latest();
}
If you need the oldest category, the same approach is applied.
public function oldestCategory() {
return $this->categories()->oldest();
}
Using only the categories relationship definition
You could also apply a different approach if you do not intend to create new methods:
$this->categories()->latest();
or
$this->categories()->oldest();
Hope it will help you.
Have a great day.
You can use latest() method
$product->categories->latest()->first();
Or,
$product->categories->orderBy('id', 'desc')->first();

Laravel: 2 DB Queries in Controller while 1 value depens on first query

I have two tables, products and subproducts.
I also have a view where I show informations about the product (View data dynamically generated from the db). On this view, all subproducts should be displayed. How can I do this? Because I loop through the first query in the view, not in the controller.
Controller:
public function showSpecificProduct($name)
{
$name = strtolower($name);
$product = \DB::select('select * from products where name = ? LIMIT 1', [$name]);
$subProducts = \DB::select('select * from subproducts where mainproduct = ?', [/* id from $product belongs here */]);
return view('products.single', ['products' => $product, 'subproducts' => $subProducts]);
}
View:
#foreach ($products as $product)
<div class="col-md-4">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ $product->name }}</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<img src="{{ $product->img }}" alt="{{ $product->name }}" class="img-responsive">
</div>
</div>
<!-- /.box -->
</div>
#endforeach
Also, is there a better method for doing the first query in laravel? Because I am using a foreach-loop while only having 1 row.
Thank you in advance
Controller
public function showSpecificProduct($name)
{
$product = Product::with('subproducts')->where('name', $name)->first();
return view('products.single')->with('products',$product);
}
View
{{ $product->name }} // shows the name of the mainproduct no need for a foreach
to show the subproducts you have to do a foreach loop (if they are many )
#foreach (($product->subproducts as $subproduct)
{{ $subproduct->name}}
#endforeach
To be able to do this you have to set relations in your models and migrations first
LIKE THIS
1-Migrations
Products migration
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->text('name');
$table->timestamps();
});
}
SubProducts migration
public function up()
{
Schema::create('subproducts', function (Blueprint $table) {
$table->increments('id');
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('subproducts');
$table->text('name');
$table->timestamps();
});
}
Refresh you migration if you dont have the right foreign key setup then
2-Models
add this to your Product model
EDIT
public function subproducts() {
return $this->hasMany('App\Subproduct', 'product_id'); // you can this
}
Subproduct model
public function product() { // i made a mistake here
return $this->belongsTo('App\Product');
}
Now feel free to use the Eloquent ORM.
You better to use Laravel Query Builder for your database queries.
In the method you used, Laravel returns a JSON object. Its hard to use for another query (I was stuck here for a week).
$product = DB::table('products')->where('name','=','$name')->pluck('id');
This returns all the IDs as an array.So you can use this for the second query inside a foreach loop.

Categories