Eloquent Injected Multiple Model Relationships - laravel

So I learned in JeffreyWay's screencasts that I can use Eloquent to get the associated id from a model injected to another model.
I'm following his series about Laravel 5.4.
Here, I have a one-to-many relationships of user to posts.
App/Post
public function user()
{
return $this->belongsTo(User::class);
}
In my User Model, I have a publish method where the Post Model is injected. The publish method is used to create a post entry into the database.
App/User
public function posts()
{
return $this->hasMany(Post::class);
}
public function publish(Post $post)
{
$this->posts()->save($post);
}
I then have a store method in my PostsController that calls the publish method inside my User Model.
PostsController
class PostsController extends Controller
{
public function __construct()
{
$this->middleware('auth')->except(['index', 'show']);
}
public function store()
{
auth()->user()->publish(
new Post(request(['title', 'body']))
);
}
}
When the publish method is called, the injected Post class automatically sets the user_id to the save method.
My question is, how do I make a relationship like this in a situation where for every posts, there are comments. These comments are associated to the Post and the User that created the comment.
In short, I should have both user_id and post_id when I call the addComment method.

User Model:
public function posts(){
return $this->hasMany(Post::class);
}
public function comments(){
return $this->hasMany(Comments::class);
}
Posts Model
public function user(){
return $this->belongsTo(User::class);
}
public function comments(){
return $this->hasMany(Comments::class);
}
Comments Model
public function post(){
return $this->belongsTo(Post::class);
}
public function user(){
return $this->belongsTo(User::class);
}
Example Problems:
1) Get user comments:
Solution: auth()->user()->comments()->get(); <- collection of user
comments .
2) Get user from the given comment:
Solution: Comment::find($someCommentId)->user()->first()->name; <-
User name from a specific comment.
3) Get all comments for a specific post .
Solution: Post::first()->comments()->get(); or eager load
Post::with('comments')->first(); <- A collection that contains post
information within it u can find a collection of comments for that
post.
4) Load user when loading a comment:
Solution: Comment::with('user')->first(); <- single collection
containing a collection with user info and comment info.
5) Load a specific user post and comments for that post at the same time:
Solution: User::with('posts.comments')->first(); <- Contains a
collection with user info and collection of all user posts with each
post containing comments.

In your question you wrote:
In short, I should have both user_id and post_id when I call the addComment method.
Which is absolutely correct and no problem. You don't have to set these properties through a method like $user->posts()->save($post) - this is just a convenience method that does the job for you (see save($model) and related setForeignAttributesForCreate($model) in the framework code; these methods just set the foreign key property for you).
In fact, the following three ways to create a new post are interchangeable:
// what you did
$user->posts->save(
new Post([
'title' => 'Hello',
'body' => 'World!',
])
);
// equivalent
Post::create([
'user_id' => \Auth::user()->id, // or \Auth::id()
'title' => 'Hello',
'body' => 'World!',
]);
// also equivalent
$post = new Post([
'user_id' => \Auth::user()->id, // or \Auth::id()
'title' => 'Hello',
'body' => 'World!',
]);
$post->save();
When storing a new comment, you will most likely have a controller like this, because a comment always belongs to a post and you therefore will need a reference of the post:
class CommentsController extends Controller
{
public function __construct()
{
$this->middleware('auth')->except(['index', 'show']);
}
public function store(Post $post)
{
$comment = new Comment(request(['body']));
$comment->user_id = \Auth::user()->id;
$comment->post_id = $post->id;
$comment->save();
}
}
You could also abbreviate it and write:
Comment::create(
array_merge(request(['body']), ['user_id' => \Auth::id(), 'post_id' => $post->id])
);

Related

laravel 8 store request with foreign key user_id not working

I would like to store the corresponding logged in user when adding a new School data. What I'm trying to do is store the logged in user_id in the schools table, in order to know on who added the school data. I have a users table already, which will establish the relation in the schools table.
My goal is when an admin is logged in, he/she can see all of the School records, otherwise if it's a user, then only fetch the records he/she added. The problem is that I can't figure out on when and where to insert the user_id data during the store request as I'm getting an error "user id field is required". Here's what I've tried so far:
Migration:
class CreateSchoolsTable extends Migration
{
public function up()
{
Schema::create('schools', function (Blueprint $table) {
$table->id();
$table->string('school_name');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
}
}
School Model:
class School extends Model
{
use HasFactory;
protected $fillable = ['school_name', 'user_id'];
public function User() {
return $this->belongsTo(User::class);
}
}
Store Request:
class StoreSchoolRequest extends FormRequest
{
public function rules(): array
{
return [
'school_name' => 'required|string|max:255',
'user_id' => 'required|exists:users,id'
];
}
}
Controller:
class SchoolController extends Controller
{
public function store(StoreSchoolRequest $request) {
$school_data = $request->validated();
$user_id = \Auth::user()->id;
$school_data['user_id'] = $user_id;
School::create($school_data );
return Redirect::route('schools.index');
}
}
Any inputs will be of big help! Thanks.
Laravel has elegant way to bind authenticated user_id. Remove user_id from request class and chaining method. Also setup relationship from User model to School Model
Form Request Class
class StoreSchoolRequest extends FormRequest
{
public function rules(): array
{
return [
'school_name' => 'required|string|max:255',
];
}
}
User Model
protected $fillable = ['school_name', 'user_id'];
...
// new line
public function schools() {
return $this->hasMany(School::class);
}
Your Controller
class SchoolController extends Controller
{
public function store(StoreSchoolRequest $request) {
auth()->user()->schools()->create($request->validated());
return Redirect::route('schools.index');
}
}
UPDATE ANSWER
Since user_id value is school name (based on image link from comment), probably there's something wrong either in User or School model. Here the quick fix
Your Controller
class SchoolController extends Controller
{
public function store(StoreSchoolRequest $request) {
auth()->user()->schools()->create(
array_merge(
$request->validated(),
['user_id' => auth()->id()]
)
);
return Redirect::route('schools.index');
}
}
You can add 'created_by' and 'updated_by' fields to your table. so you can register in these fields when additions or updates are made.
Then you can see who has added or updated from these fields.
class School extends Model
{
use HasFactory;
protected $fillable = ['school_name', 'user_id', 'created_by', 'updated_by'];
public function User() {
return $this->belongsTo(User::class);
}
}
Your controller part is correct but since you get the logged in user, you wont be having user_id in the request. So you should remove the rules about user_id from your StoreSchoolRequest.
class StoreSchoolRequest extends FormRequest
{
public function rules(): array
{
return [
'school_name' => 'required|string|max:255'
];
}
}
Problem is here ..
$school_data = $request->validated();
Since you are using $request->validated()..
You have to safe()->merge user_id into it , here Docs : .
$validated = $request->safe()->merge(['user_id' => Auth::user()->id]);
Then put this $validated into create query , Thanks. –

Laravel how to save belongstomany in store controller?

I can save my ticket inside a ticket table. But i also have a ticket_user table with inside a ticket_id and a user_id. So when the user press save it need to automaticlay pick also the ticket_id and the user_id inside the ticket_user table. I have a many to many table between Users and Ticket.
this is the error i get Call to a member function attach() on null
User Models
public function ticket(){
return $this->belongsToMany(Ticket::class, 'ticket_user');
}
Ticket models
public function users() {
$this->belongsToMany(User::class, 'ticket_user');
}
Controller
public function store(Request $request)
{
$this->validate($request, array(
'title' => 'required',
'ticket' => 'required'
));
$ticket = new Ticket;
$ticket->title = $request->title;
$ticket->ticket = $request->ticket;
$ticket->save();
$ticket->users()->attach($request->input('user_id'));
return redirect()->route('users.tickets.index');
}
You are not returning anything in users function in Ticket class.
public function users() {
return $this->belongsToMany(User::class, 'ticket_user');
}

How to create Laravel api route endpoint/query parameters?

I would like to be able to filter the json response using some fields in the database e.g. api/v1/user?username=mary but don't know how to do this. My second question is that the route api/v1/user/3 is working but I can't remember how I set this up as I did it some time ago. Can anyone help?
api.php
Route::group(['prefix' => 'v1'], function () {
Route::apiResource('/user', 'UserController');
});
user resource
public function toArray($request)
{
return parent::toArray($request);
}
user controller
public function show(User $user): UserResource
{
return new UserResource ($user);
}
public function index(): UserResourceCollection
{
return new UserResourceCollection(User::orderBy('created_at', 'desc')
->paginate(5)
);
}
public function store(Request $Request)
{
$request->validate([
'first_name' => 'required',
'last_name' => 'required',
'email' => 'required',
]);
$user = User::create($request->all());
\Mail::to($user)->send(new Welcome);
return new UserResourse($user);
}
public function update(User $user, Request $request): UserResource
{
$user->update($request->all());
return new UserResource($User);
}
public function destroy(User $user)
{
$user->delete();
return response()->json();
}
}
UPDATED
I have seen several tutorials with the advice that the user has given below but I don't know how to put it together because I already have a show method above. I tried commenting out the show method and creating another show method for the query string
public function show(User $user): UserResourceCollection
{
$request->input('username');
return new UserResourceCollection(User::orderBy('created_at', 'desc')
->where('username', '=', $username)
->paginate(2)
);
}
and added a GET route
Route::group(['prefix' => 'v1'], function () {
Route::apiResource('/user', 'UserController');
Route::get('/user/{username?}','UserController#show');
});
This is working as an endpoint. The pagination part is working I made it smaller so I know it's calling the method but it is searching by id and i want to search by username. Ideally I want to use a query parameter like api/v1/user?username=mary.
To get json request, you can call $request->input('name') for example.
You can setup the resource action on controller. By example, you can create UserController.show method. So, the GET /user/{id} method will be handled by UserController.show
For more example, you read the documentation.
I don't think it's possible to create api url parameters. I started again using this tutorial
https://www.youtube.com/watch?v=z3YPhYwcbBM.
This way means I always have to know the id (search by product id rather than filtering reviews by query search) which isn't ideal
e.g. http://localhost:8000/api/v1/products/2/reviews
however I can add more endpoints in place of reviews like categories etc.

Laravel: How to use the same post form for multiple sections

I want to build something like facebook and I have a form where the user can post content but I want to use the same form on different sections, for example: groups, pages, profile.
I have PostController that is a resource that receives the post requests but I need a way to differentiate between sections in order to store the data with the correct section_type and section_id.
// Post Model Post.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['content', 'user_id'];
public function section()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo(User::class);
}
public function group()
{
return $this->belongsTo(Group::class);
}
public function page()
{
return $this->belongsTo(Page::class);
}
}
The User.php Model
class User extends Authenticatable
{
public function posts()
{
return $this->morphMany(Post::class, 'section');
}
}
and this is the PostController and store() function where I have only one situation at this moment where a post is stored in section_type App\User, but I need a way to store it in App\Group or App\Page also.
public function store(Request $request)
{
$this->validate(request(),
[
'content' => 'required|min:5',
]);
$user = User::find(Auth::id());
$user->posts()->create([
'content' => $request->content,
'user_id' => Auth::id()
]);
return redirect('/');
}
What do I need to do in PostController.php?
I thought maybe I could use a post request with parameters in the form, like this for posting in groups
<form method="post" action="/?group=1">
or this for posts in pages
<form method="post" action="/?page=1">
And after that use $request->query() to get the section and id. Do you have other ideas?

Adding records to a relations repository with Laravel

I’m using laravel 5.5 and andersao/l5-repository. I want to add an answer record with the question repository.
My Question Controller
protected $repository;
public function __construct(QuestionRepository $repository)
{
$this->repository = $repository;
}
public function add_answer(AnswerAddRequest $request)
{
$this->validator->with($request->all())->passesOrFail(ValidatorInterface::RULE_CREATE);
$question = $this->repository->answer->create([
'content' => 'Answer text question',
'user_id' => Auth::user()->id
]);
return question;
}
My Question Model
public function answer()
{
return $this->belongsTo(Answer::class, 'question_id');
}
I tried '$this->repository->answer()'
always error : Undefined property
I think I have to use the with() method, but I don’t want to take all the content. I only need to add content with the relation of the model.
You should add this part of code
public function answer()
{
return $this->belongsTo(Answer::class, 'question_id');
}
into your Question mode, not into your repository.
The Laravel Eloquent class extends the Model class, which has methods like belongsTo that you're using.
--- EDIT ---
If you want to use repository, you could integrate it with Eloquent:
$this->reposotiry->find($request->input('question_id'))->answer()->create([
'content' => 'Answer text question',
'user_id' => Auth::user()->id
]);

Resources