Laravel sortby after select specific option - laravel

I would like to add a select field in my application with the option of choosing to sort products by date of addition or price. After selecting a specific option, e.g. sort by price, products returned from the cheapest are returned. I know how to sort products but I do not know how to do it to show sorted products after selecting a specific option, e.g. sort by price. My code :
web.php
Route::get('/{category}', 'ProductController#index');
ProductController:
public function index()
{
$products= Product::latest()->get();
return view('category')->with('products', $products);
}

In your view you could use a form like this to get wich category the user wants to filter:
<form method="get" action="{{route('product.index')}}">
#csrf
<select name="category">
<option value="price">Price</option>
<option value="date">Date</option>
</select>
<button type="submit">Search</button>
</form>
And in your controller you can filter the collection using the selected option:
public function index(Request $request)
{
$products= Product::all()->sortBy($request->category);
return view('category')->with('products', $products);
}

Related

How I can calculate and output the properties by getPropertyAttribute in laravel?

I prefer to get the total price of each product in the model by using the getAttribute feature of Laravel without a database column.
The Order model hasmany items relation and the items table has price and qty properties.
I want to calculate the total in the Order model as:
protected $appends = ['total'];
public function getTotalAttribute()
{
return $this->items->map(function ($v) {
return $v->qty * $v->price;
}); also with ->first();
}
in view:
#foreach($orders as $order)
<p>{{ $order->total }}
/* this total comes from model total attribute
#endforeach
But get an error and in the ->first() state I get just the first one total value.
is there any way to calculate total in the Order model or I must use a column in the items table?
I think that method name is a bit confusing, so I would suggest to use this method name:
public function getItemTotalsAttribute(){
return $this->items->map(function ($item) {
return $item->qty * $item->price;
});
}
Then in your blade file do this:
#foreach($orders as $order)
#foreach($itemTotal as $order->itemTotals)
{{ $itemTotal }}
#endforeach
#endforeach

Laravel sort and/or group from relationship

In livewire, I'm getting a collection of records ($testItems) from a many-many relation like:
---blade---
<livewire:test-items :test="$test"/>
---test model---
public function items ()
{
return $this->belongstoMany(item::class);
}
---livewire component---
render(){
$testItems = $this->test->items;
return view ('livewire.test-items',['testItems' => $testItems]);
}
---test-items livewire component blade---
#foreach ($testItems as $testItem)
<div> {{$testItem->question}}</div>
<div> {{$testItem->category->name}}</div>
<div> {{$testItem->answer}}</div>
#endforeach
Each item also has other fields like 'category' and I'd like to sort or group the display of these testItems in the component by category. What's the eloquent syntax for this?

Laravel Insert Data to Pivot Table From blade.php

I wanted to know what modifications should I do to this form to be able to link the newly added question to a group for example question that I am adding now says what's your name? I want it to be linked to group one so that only users in group 1 can see it
this is my Question Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Question extends Model
{
public function answer()
{
return $this->hasMany('App\Answer')->latest();
}
public function userAnswer(){
return $this->hasOne('App\Answer')->where('user_id', Auth()->id())->latest()->first();
}
public function group()
{
return $this->belongsToMany(Group::class);
}
}
This is my Group Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Group extends Model
{
public function question()
{
return $this->hasMany('App\Question');
}
}
This is my Group_Question Table ( Pivot table containing question_id and group_id )
Schema::create('group_question', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('group_id');
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
$table->unsignedBigInteger('question_id');
$table->foreign('question_id')->references('id')->on('questions')->onDelete('cascade');
$table->timestamps();
});
and finally this is the form that I use to add Questions to the questions table:
<form class="" action="/questions/create" method="post">
#csrf
<div class="form-group">
<input type="text" name="question" value="" class="form-control" placeholder="Please Enter Question">
</div>
#can('add', \App\Question::class)
<div class="form-group">
<button type="submit" name="add" class="btn btn-success float-right">Add Question</button>
</div>
#endcan
</form>
I would really appreciate it if you can edit my from to be able to assign the question to a group in the pivot table.
You need to add a multi select in order to be able to assign groups to a Question. Since $question->group is a belongsToMany relation, you can obvisouly assign a question to multiple groups. You can do something like this:
<select name="groups[]" multiple="multiple">
#foreach ($groups as $group)
<option value="{{ $group->getKey() }}">{{ $group->name }}</option>
#endforeach
</select>
Caveat:
$groups should be exported by the create action of your QuestionController and be something like $groups = Group::get(). Alternatively you could write #foreach (Group::get() as $group) in your blade template, but keeping as much logic out of your template as possible is a good habit to get into.
$group->name is just a guess, not knowing how your group table looks like
The example HTML is just the bare minimum to get you started, you should make sure that on validation error etc. the old values are prefilled. I leave that to you as an excercise.
Next, go to your QuestionController and link the question to the appropriate groups by adding the following line to your store method:
$question->group()->attach($request->input('groups', [])));
Bonus remarks:
To follow Laravel best practices, the group relation in the Question model should be studly case and plural, as it is a BelongsToMany relation. Thus, you should rename it to Groups().
The answer() method should also be called Answers().
It is a little weird that you define a new HasOne relationship within the userAnswer() getter. Why not implement a getter or custom attribute for the current user's Answer instance instead by doing something like
public function getUserAnswerAttribute(): ?App\Answer
{
return $this->Answer()->whereUserId(Auth()->id())->first();
}
(if you are not familiar with attributes: you can then just call $question->userAnswer to get the result).
Can each user really only post one Answer to each Question?
You might want to use <form action="{{ route('question.create') }}" method="POST"> instead of hardcoding the URL. That's what named routes are there for.

How should I approach making this form in Laravel 5.2?

When I started this project I avoided using Laravel's form helper because it seemed like a convoluted way to make a form that didn't increase readability at all. I now wish that I had because model form binding is a lot harder than I anticipated.
This project is a blog website and posts have been set up to have a many-to-many relationship with tags (models posted at the bottom along with table schemas). When I go to edit a post, I want to have the tags associated with that post already selected in the field, with the option to remove them as well as add new tags. This is what I have to start:
<div class="form-group">
<select class="form-control select2-multi" name="tags[]" multiple="multiple">
#foreach($post->tags as $tag)
<option value="{{ $tag->id }}" selected>{{ $tag->name }}</option>
#endforeach
#foreach($tags as $tag)
<option value="{{ $tag->id }}">{{ $tag->name }}</option>
#endforeach
</select>
</div>
I realized I have a problem in that tags that are printed out as selected also get printed out in the second foreach.
At this point I am stumped. I have two ideas for what to do but I want to follow what would be the best practice so any advice is appreciated:
Use procedural programming in the controller to remove any tags from the tag array that match the $post->tags tags before passing it to the view.
Create a method in the tags controller that builds a query to select all tags except those associated with a post who's ID is passed as a parameter.
My idea for a SQL query that would do this (but I am unsure how to do this in eloquent):
SELECT *
FROM tags
WHERE id NOT IN( SELECT tag_id
FROM posts INNER JOIN post_tag ON posts.id=post_tag.post_id)
Am I making this problem more complicated than it is? Should I just use the form helper to bind the data to my form?
--- Post and Tag Models plus DB Schemas ---
Post model
class Post extends Model
{
protected $table = 'posts';
/**
* Define relationship between posts and categories.
*
* #return eloquent relationship
*/
public function category()
{
return $this->belongsTo('App\Category', 'category_id');
}
/**
* Define relationship between posts and tags.
*
* #return eloquent relationship
*/
public function tags()
{
return $this->belongsToMany('App\Tag', 'post_tag', 'post_id', 'tag_id');
}
}
Tag model
class Tag extends Model
{
protected $table = "tags";
public $timestamps = false;
public function posts()
{
return $this->belongsToMany('App\Post', 'post_tag', 'tag_id', 'post_id');
}
}
Schemas
posts(id, title, body, slug, category_id, created_at, updated_at)
tags(id, name)
post_tag(id, post_id, tag_id)
This will be my suggestion.
In you controller
public function edit(Post $post){
$tags = Tag::all();
$postTags = $post->tags->pluck('id')->toArray();
return view('edit', compact('tags', 'postTags'));
}
In your blade
...
#foreach($tags as $tag)
{{--We list all the tags--}}
{{--While listing them, we check if the current tag is part of the tags belonging to this post--}}
{{--If it belongs then we select it--}}
<option value="{{ $tag->id }}" {{ in_array($tag->id, $postTags) ? "selected" : null }}>{{ $tag->name }}</option>
#endforeach
...

How to safely access other users and other model's records inside Blade template

I have 3 models in my app Users, Sales and Plans, when I render sales for each customer (due to storing) I only get id's for other users and models related to that sale (like account manager, owner, plan), now I'm trying to use those ID's inside blade to get names or other rows based on ID and model. Here is the show function:
public function show($id) {
$user = User::find($id);
$sales = Sale::where('customer_id', '=', $id)->get();
return view('profiles.customer', ['user' => $user, 'sales' => $sales]);
}
And in blade I get all those sales like:
#foreach ($sales as $sale)
<li>
<i class="fa fa-home bg-blue"></i>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> {{$sale->created_at->format('g:ia, M jS Y')}}</span>
<h3 class="timeline-header">{{$user->name}} became a customer</h3>
<div class="timeline-body">
<p>Date: {{$sale->sold_date}}</p>
<p>Price: {{$sale->sale_price}}</p>
</div>
</div>
</li>
#endforeach
So inside each record I have like "account_manager_id", "agent_id", "owner_id", "plan_id".
Currently I have this solved by adding public static function (this is for users, have same function for Plan model as well) in Sale model class:
public static function getUser($id) {
return User::where('id', $id)->first();
}
And I'm using it like this in Blade:
Account manager: {{$sale->getUser($sale->account_mgr_id)->name}}
Is this the safest and best way to do it? Or there is something I'm overlooking here?
You need to add relationships in your Sales Model.
class Sales extends Eloquent {
.....
.....
public function accountManager() {
return $this->hasMany('App\User', 'account_manager_id');
}
public function agents() {
return $this->hasMany('App\User', 'agent_id');
}
public function owner() {
return $this->hasOne('App\User', 'owner_id');
}
}
Now $sales->agents will give you a user with agent_id as id in User table.
Update your hasOne, hasMany relationships as your need. Laravel Documentation.
From your blade template, your access your AccountManager as
#foreach($sales->accountManager as $manager)
Name: {{ $manager->name}}
#endforeach
I think you could use Eloquent relationships. Taking your example, you should define relationship in your User model:
<?php
class User extends Eloquent {
public function sales() {
return $this->hasMany(Sale::class, 'customer_id');
}
}
Then, whenever you need to get sales of that user (entries, that relate via customer_id column), just simply do
<?php
$user = User::find($id);
$sales = $user->sales;
This is very fun when when you have to print out list of users that have sales, for example
<?php
public function showUsersWithSales() {
$users = User::with('sales')->get();
return view('users-with-sales', compact('users'));
}
users-with-sales.blade.php example:
#foreach ($users as $user)
User: {{ $user->name }}<br>
Sales:<br>
#foreach ($user->sales as $sale)
{{ $sale->amount }} {{ $sale->currency }} # {{ $sale->created_at }}<br>
#endforeach
<hr>
#endforeach
It would print all users with their sale amount and currency, followed by date when it was created.
This sample takes into account, that your User model has name attribute and your Sale model has amount, currency, created_at and customer_id fields in your database.
To reverse the action, say you have a sale and want to know who made it, just simply define a relationship!
<?php
class Sale extends Eloquent {
public function customer() {
return $this->belongsTo(User::class, 'customer_id');
}
}
Hope that helps!
Eloquent Relationship is your friend, https://laravel.com/docs/5.2/eloquent-relationships and you can solve your problem easily.
Suggestion is to remove all those function access and control from view and put it somewhere else. This will be good habit for you so you can avoid the infamous fat view.

Resources