How to create Laravel api route endpoint/query parameters? - laravel

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.

Related

API Postman error: "The PATCH method is not supported for this route. Supported methods: GET, HEAD"

I create custom Request which named "StoreUser" for custom validation rules for store and update methods. For store method when i using POST method in Postman it's all working good. But for PATCH/PUT method i catch error: "The PATCH method is not supported for this route".
Supported methods: GET, HEAD". My URL for PATCH method: http://127.0.0.1:8000/api/users/44
Using debagger, i found that the problem occurs when custom Request "StoreUser" start return array rules in rules() method.
Below my code. Only error occurs in PATCH/PUT method, POST it's ok
ApiResource
Route::apiResource('users', 'UserController');
UserController update/store methods
public function store(StoreUser $request)
{
$request->validated();
$password = User::hashPassword($request->get('password'));
$request->merge(['password' => $password]);
$user = User::create($request->all());
return response()->json($user, 201);
}
public function update(StoreUser $request, $id)
{
$request->validated();
$user = User::find($id);
$user->update($request->all());
return response()->json($user, 200);
}
Custom Request StoreUser
public function rules()
{
return [ // in this place error occurs ONLY IN PATCH/PUT methods
'name' => 'required|min:5',
'email' => 'required|email|unique:users',
'password' => 'required|min:6|max:50'
];
}
Have you tried adding _method="PATCH" in the body of the request. The type of request should be POST.
Try to romve $request->validated(); when you use custom valdation class then
there is no need to call validated() method
public function update(StoreUser $request, $id)
{
$request->validated();
$user = User::find($id);
$user->update($request->all());
return response()->json($user, 200);
}
use following code
public function update(StoreUser $request, User $user)
{
$user->update($request->all());
return response()->json($user, 200);
}
In above code User $user use as parameter its mean that route model binding so
there is no need to use extra query to find user

Multiple update methods in same controller Laravel 6

I am currently trying to build a user registration system with edit fields. At the edit portion, I had to create separate views for editing/updating personal details, email, and passwords.
I started with an empty resource controller. it had only one edit method. Hence I added additional edit methods. Each method can have a separate route. However, I have a hard time having a separate route for each update method in each section as the resource has only one route like this in docs:
PUT/PATCH /photos/{photo} update photos.update
Is there any workaround for this?
Controller
class UserController extends Controller
{
public function __construct()
{
$this->middleware(['auth', 'verified']);
}
public function index()
{
return view('users.index');
}
public function edit_personal(User $user)
{
$user_profile = User::find($user->id);
return view('users.edit.personal', ['users' => $user_profile]);
}
public function update_personal(Request $request, User $user)
{
// How to write route for this method.
}
public function edit_email(User $user)
{
$user_profile = User::find($user->id);
return view('users.edit.email', ['users' => $user_profile]);
}
public function update_email(Request $request, User $user)
{
// How to write route for this method.
}
public function edit_password(User $user)
{
$user_profile = User::find($user->id);
return view('users.edit.password', ['users' => $user_profile]);
}
}
Routes
Auth::routes(['verify' => true]);
Route::get('/', function () {
return view('welcome');
});
Route::get('/users/{user}/personal', 'UserController#edit_personal')->name('users.personal');
Route::get('/users/{user}/email', 'UserController#edit_email')->name('users.email');
Route::get('/users/{user}/password', 'UserController#edit_password')->name('users.password');
Route::resource('users', 'UserController');
Basically I have separated edit portion of user controller into personal, email and password sections and they have separate forms. I want to write update functions for each section in UserController.
don't know why are you using separate forms for updating each fields while you can do it in a single form. however you can use either put/patch or post method for updates. here's i am using post for example.
routes:
Route::get('users/{user}/personal', 'UserController#edit_personal')->name('users.personal');
Route::post('users/{user}/personal', 'UserController#update_personal')->name('users.update-personal');
Route::get('users/{user}/email', 'UserController#edit_email')->name('users.email');
Route::post('users/{user}/email', 'UserController#update_email')->name('users.update-email');
Route::get('users/{user}/password', 'UserController#edit_password')->name('users.password');
Route::post('users/{user}/password', 'UserController#update_password')->name('users.update-password');
as you are using route model binding you can directly get the object.
public function edit_personal(User $user)
{
return view('users.edit.personal', ['users' => $user]);
}
public function update_personal(Request $request, User $user)
{
//validation goes here
$user->update([
'value' => $request->value,
...........
]);
}

Getting all posts from a user

I'm really new to Laravel and ran it some problems. I have a /user/USERNAME view, which obviously, shows the users profile. But now I want to get ALL posts the user posted to be shown on his profile.
My web.php
Route::group(['middleware' => 'auth'], function() {
Route::any('/user/{username}', [
'as' => 'user',
'uses' => 'UserController#view_user'
]);
});
My User model
public function posts() {
return $this->hasMany(Post::class);
}
My Post model
public function user() {
return $this->belongsTo(User::class);
}
My UserController
public function view_user($username = null) {
$user = null;
$user = User::where('name', $username)->firstOrFail();
$posts = Post::where('creator', $username)->get();
return view('/user', [
'user' => $user,
'posts' => $posts
]);
}
public function index() {
$posts = Post::all();
return view('user', compact('posts'));
}
When I'm trying to get the posts with for example {{ $posts->title }} it gives me: Property [title] does not exist on this collection instance.
How am I supposed to do this the correct way?
$posts returns a collection, so in order to retrieve the title of a post you need to loop through the single items of the collection. Then you can use $post->title.

Eloquent Injected Multiple Model Relationships

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

Laravel - return variable from Form Requests to Controller

How can I return a variable from Form Requests (App\Http\Requests) to Controller (App\Http\Controllers)?
I am saving a record on function persist() on Form Requests.
My goal is to pass the generated id so that I can redirect the page on edit mode for the user. For some reason, the Controller cannot receive the id from Form Requests.
App\Http\Requests\MyFormRequests.php:
function persist()
{
$business = Business::create([
'cart_name' => $this['cart_name'],
'product' => $this['product']
]);
return $myid = $business->id;
}
App\Http\Controllers\MyControllers.php:
public function store(MyFormRequests $request)
{
$request->persist();
return redirect()->route('mypage.edit.get', $request->persist()->$myid);
}
Important
I must add that this is not the recommended way. Your FormRequest should only be responsible for validating the request, while your Controller does the storing part. However, this will work:
App\Http\Requests\MyFormRequests.php:
function persist()
{
return Business::create([
'business_name' => $this['business_name'],
'nationality' => $this['nationality']
])->id;
}
App\Http\Controllers\MyControllers.php:
public function store(MyFormRequests $request)
{
$id = $request->persist();
return redirect()->route('register.edit.get', $id);
}
A guy name Snapey helped me:
public function store(MyFormRequests $request)
{
$business = $this->persist($request);
return redirect()->route('register.edit.get', $business->id);
}
private function persist($request)
{
....
return $business;
}
hope this could help someone in the future.

Resources