Laravel Insert Data to Pivot Table From blade.php - laravel

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.

Related

Relationship between two tables - Laravel, Inertia, Vuejs

I'm new to the subject, I have two tables related to each other, one for departments and one for users, I want to know how many users there are in each department
DepartmentController.php
public function index()
{
return Inertia::render('back/app/departments/index', [
'filters' => request()->all('visibility', 'status', 'search'),
'departments' => Department::orderBy('name')->get(),
'total_members' => User::where('department_id')->count(),
dd(User::where('department_id')->count()),
]);
}
Index.vue
<td class="flex items-center text-sm leading-5 text-gray-500 px-6 py-4 whitespace-no-wrap">
<span class="font-medium">{{ department.total_members }}</span> {{ $trans('labels.members') }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-right text-sm leading-5 font-medium">
<inertia-link :href="route('app:projectdepartment.index', {id: department.id})">
{{ $trans('labels.view-activity') }}
</inertia-link>
</td>
If you have the users relationship set up on your Department model, you could use the withCount() method:
Department::withCount('users')->orderBy('name')->get()
This will give you a users_count property on each department.
Assuming you have the relationships defined on each model as noted, you could also define an accessor or as was already stated get the number of related models by way of withCount or also with loadCount after retrieving an individual Department model.
Models/User.php
public function department()
{
return $this->belongsTo(Department::class, 'department_id');
}
public function getActiveUserCountAttribute($value)
{
// Perform additional logic on the users relationship
return $this->users()->where('active',1)->count();
}
Models/Department.php
public function users()
{
return $this->hasMany(User::class);
}
Remember that one of the benefits of withCount is that it doesn't load the related models, avoiding an n+1 problem. If you use withCount, you wouldn't need the accessor defined on the Department model but would need the User relationship defined on Department.
Note that you could also use the loadCount method after you have already retrieved the Department model, e.g. for an individual show page as opposed to a collection.
// From your show method in your controller
$department = Department::where('name',$request['name'])->first();
$department->loadCount('users');
To eager load the 'active_user_count' attribute on each Department model, you would use the $with property. With this defined, you would not need to use the withCount or similar in your controller method.
// Department.php
// A relationship that should always be loaded with every Department request.
protected $with = ['active_user_count']
Laravel docs:
https://laravel.com/docs/8.x/eloquent-relationships#counting-related-models

Merge two collections in Laravel and access it

Ok, so here is my problem : I have'nt found a proper solution to 'merge' (not sure if it is the right word for it) two collections in laravel. I'll show you my code, it'll explains itself.
So here is my crappy way-arround code, but I'm pretty sure there is a better way to do it :
Here is my controller :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product;
use App\ProductPhoto;
class ProductController extends Controller
{
public function index() {
$products = Product::all();
$photos = ProductPhoto::all();
return view('/products')->with([
'products' => $products,
'photos' => $photos
]);
}
}
and my view looks like this ;
#foreach($products as $product)
#foreach($photos as $photo)
#if($photo->product_id = $product->id)
<img src="/products/{{ $product->id }}/{{ $photo->filename }}">
#endif
#endforeach
#endforeach
But I'd like to have all the photos related to the product in the product collection. I'd like to have a simple view as :
#foreach($products as $product)
#foreach($product->photos as $photo)
<img src="/products/{{ $product->id }}/{{ $photo->filename }}"
#endforeach
#endforeach
Or another way simpler than that. I've been trying merge, but I don't think this is the one I need, I don't know. Or maybe the way I do it is the correct one, but I doubt it.
Thanks for reading !
I'd recommend using eloquent relationships for this. To do this you would want to create a relationship on the product class called photos. This would look something like this:
class Product extends Model
{
...
public function photos()
{
return $this->hasMany(ProductPhoto::class);
}
...
}
Once you have done this, you can update your controller as you no longer need to fetch the photos from the model. For performance, you could eager load the photos.
public function index() {
$products = Product::with('photos')->get();
return view('/products')->with([
'products' => $products
]);
}
Finally, you can update your view to use the photos relationship instead.
#foreach($products as $product)
#foreach($product->photos as $photo)
<img src="/products/{{ $product->id }}/{{ $photo->filename }}"
#endforeach
#endforeach
I hope that helps.
I don't usually like when people ignore the proposed example and tell you that there's a better way to do that... but there is a better way to do that:
for the Products Eloquent add a method called photos
Class Products extends Model{
...
public function photos(){
return $this->hasMany(Photos::class);
}
}
for the Photos Eloquent add a method called product
Class Photos extends Model{
...
public function product(){
return $this->belongsTo(Product::class);
}
}
now for the photos migration you need to add a column called product_id in case you're using this photos table only for products and not anything else like users.
if you want to be able to assign photos to multiple models like Users can have photos, then you need to consider using Morphs Polymorphic Relationships
when you want to add photos to the product you can then do
Product::find(1)->photos()->create(['filename'=>'....']);
That should add photos row assigned with the product.
Then in your view you can do something like:
#foreach(Products::all() as $_product)
<div>
{{$_product->title}
<div class="product_photos">
#foreach($_product->photos as $_photo)
<img .../>
#endforeach
</div>
</div>
#endforeach
I'm just showing you the correct way to achieve that instead of comparing photos with id and so on, your way will make it much slower.
Look up Laravel Relationships. That will take a bit of time like few minutes to convert your models to use Relationships but will save you days of coding later.

Laravel sortby after select specific option

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);
}

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