Get the bound route model instance in a generic parent class - php

I'm using Laravel 5.1's route model binding to infer/instantiate the correct model instance from the routes.
I don't want to rewrite a show() method for each resource, and would rather call parent show() method in a generic parent class. The problem is, I don't know how to allow my parent class's show() method to infer the correct model instance, so I'm forced to create a show() on each child resource and specify the model (like below, User $user).
How can I get the bound model in my controllers from the binding in RouteServiceProvider: App\User? And pass it through to the parent show() method?
Route:
// Passing in id 1 for {user}
$router->get('/users/{user}', 'Resources\Users#show');
RouteServiceProvider.php:
public function boot(Router $router)
{
parent::boot($router);
$router->model('user', 'App\User');
}
UsersController.php: How I am currently doing it
class User extends ResourceController
{
public function show(Request $request, User $user)
{
// Returns user of ID 1 as expected
return $user;
}
ResourceController.php: How I'd like to do it
class ResourceController extends Controller
{
public function show(Request $request, Model $model)
{
// Infer "Users" model here and returns User with ID 1
return $model;
}

Related

How to access method in eloquent model of current user

Everything what I was searching tells me about Auth::user() or auth()->user() but it gives access to result of query to DB - to all fields of record.
Here are details.
Laravel 8.x
There is eloquent model called User. I created also another elo model Example. There is relation in database one-to-one so table examples has foreign key user_id. In User model I created
public function example() { return $this->hasOne(); }
and in Example:
public function user() { $this->belongsTo(); }
Now I have ExampleController with public function __contruct() - if current user doesnt have related example yet I want to run view to create it.
Tell me please what is a correct way to do that - to access current user model method?
You could use an inline middleware
use App\Models\Example;
use Illuminate\Support\Facades\Auth;
class ExampleController extends Controller
{
/**
* Instantiate a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
if ( Example::where('user_id', Auth::id())->doesntExist() ) {
return redirect('user-has-no-example');
}
return $next($request);
});
}
}

Can't access property of extended class with new instance throught ajax Laravel

I will give below code example to better explain:
class BaseController extends Controller
{
public $globalCurrencies;
public $currentCurrency;
public $globalLanguages;
public $currentLanguage;
public function __construct()
{
$this->globalCurrencies = $this->getCurrencies();
$this->globalLanguages = $this->getLanguages();
$this->middleware(function ($request, $next) {
$this->currentCurrency = $this->getCurrentCurrency();
$this->currentLanguage = $this->getCurrentLanguage();
return $next($request);
});
}
CartController
class CartController extends BaseController
{
public function __construct()
{
parent::__construct();
}
BaseController sets up base variables for the app. The cart is using some of them like (current currency). Some of the variables are session based so in base construct there is middleware used to get session data in the constructor). For this part, everything works and cart has access to baseController properties.
Problem occurs here:
class OrderController extends BaseController
{
public function loadPaymentsAndDelivery(Request $request)
{
$cart = new Cart;
dd($cart->globalCurrencies) // WORKS
dd($cart->currentCurrency) // NULL
}
}
Basically, on a new Cart instance, I can access every property created without middleware. Without middleware, I cannot access the session to set up the cart. Method loadPaymentsAndDelivery is loaded via ajax but I tried directly call the method and the properties were still null.
Can somebody explain why this is happening?

Get the morphMany relationship to a model from the user

Let say i have the following;
User Model;
class User extends Authenticatable
{
public function posts()
{
return $this->hasMany('App\Models\Socials\Post');
}
}
Post Model;
class Post extends Model
{
public function comments()
{
return $this->morphMany('App\Models\Socials\Comment', 'commentable');
}
Comment model;
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
When i used $user = User::find($id); and $user->posts(), it returns all the post of the user, but if i used this method $user->posts()->comments() It return this message Method Illuminate\Database\Query\Builder::comments does not exist.
The question is how can i get all the comments of the user on the said post?
Change:
$user->posts()->comments();
to:
$user->posts->pluck('comments')->collapse();
The method itself returns an instance of Eloquent's query builder allowing you to add to or edit the query if you want. However, if you don't want to edit the query you can access the relationships as properties and Laravel will handle to execution of the query.
Essentially, $user->posts is actually turning into $user->posts()->get() in the background.
Credit to #JonasStaudenmeir.

Laravel 5 variable type hinting for requests

I'm trying to build a very general CRUD for Laravel, but I'm stuck at validation.
I have a RESTful resource controller, that will handle basic operations for any entity:
class CrudController extends Controller {
public function store(StoreRequest $request) {...}
public function update(UpdateRequest $request) {...}
...
}
Each actual entity has its own controller that extends it, say:
ArticleController extends CrudController
CategoryController extends CrudController
TagController extends CrudController
So when you call the create() method in ArticleController() it basically calls CrudController::create().
So far so good. Now with the validation issue:
For the Article entity, I'd have the validation rules defined in:
app/Http/Requests/StoreArticleRequest.php
app/Http/Requests/UpdateArticleRequest.php
But classes for requests are already defined in CrudController, so Laravel will use those for type-hinting, so the executed validation will be the one from StoreRequest instead of the one I want, StoreArticleRequest.
How do I pass these classes to the create() and update() methods in CrudController?
CAN'T DO:
I don't want to redefine the create() method in ArticleController, because copy-pasting the create() function logic in each EntityController would lead to duplicated code. Also, they would have different parameters, which would trigger a "should be compatible with" PHP error.
ALREADY TRIED:
In CrudController:
use App\Http\Requests\StoreCRUDRequest as StoreRequest;
use App\Http\Requests\UpdateCRUDRequest as UpdateRequest;
class CrudController extends Controller {
public function store(StoreRequest $request) {...}
public function update(UpdateRequest $request) {...}
...
}
In ArticleController:
use App\Http\Requests\StoreArticleRequest as StoreRequest;
use App\Http\Requests\UpdateArticleRequest as UpdateRequest;
class ArticleController extends CrudController {
...
// the create() and store() methods are no longer defined here
}
But it doesn't work, for ArticleController Laravel still runs the validation from App\Http\Requests\StoreCRUDRequest, defined in CrudController.
Any idea how I can make this happen?
Rather than type hinting, you can use the resolve() helper to resolve the FormRequest class. See the FormRequestServiceProvider on Line 33 and the helper docs
abstract class CrudController extends Controller {
protected $modelClassName; # Name of your model class
protected $storeRequest; # Name of your store FormRequest class
protected $updateRequest; # Name of your update FormRequest class
public function store(): Model {
$modelClassName = $this->modelClassName;
$request = resolve($this->storeRequest);
$instance = $modelClassName::create($request->validated());
return $instance;
}
public function update($id): Model {
$modelClassName = $this->modelClassName;
$instance = $modelClassName::find($id);
$request = resolve($this->updateRequest);
$instance->fill($request->validated())->save();
return $instance;
}
}
Any reason against passing the Request objects into the constructor and letting Laravel inject them at runtime?
Example:
<?php
class ArticleController extends CrudController {
use CreateOperation;
public function __construct(StoreArticleRequest $storeRequest, UpdateArticleRequest $updateRequest)
{
$this->storeRequest = $storeRequest;
$this->updateRequest = $updateRequest;
parent::__construct();
}
}
trait CreateOperation
{
/**
* #var Request
*/
private $storeRequest;
public function setStoreRequest(Request $storeRequest): void
{
$this->storeRequest = $storeRequest;
}
public function storeCrud() // parameter removed
{
$request = $this->storeRequest;
...
...
}
}

Determining Proper Class Scope PHP

I have a model class ModelHome that is a child of Model ie:
class ModelHome extends Model
Model is a variable of the Controller class ie:
class Controller {
public $model;
public function __construct () {
$this->model = new Model;
}
}
Is it possible to access a method within the Controller class from within a method inside the ModelHome class?
I've tried parent:: and calling the class by name ie Controller::method but I can't seem to find the right scope to access the method I need.
Thanks.
-Vince
First of all, you must have an instance of ModelHome. If you make an instance of Model, that has not automatically been extended by ModelHome just because ModelHome exists. So, i guess your Controller::__construct() should be:
public function __construct () {
$this->model = new ModelHome;
}
However, your ModelHome does not know about your Controller class/instance. You could make a __construct in ModelHome that takes a parameter with a link to the controller. Like this:
class ModelHome extends Model {
public $controller;
public function __construct ($controller) {
$this->controller = $controller;
}
}
class Controller {
public $model;
public function __construct () {
$this->model = new ModelHome($this);
}
}
Now, your ModelHome knows about the controller by using $this->controller

Categories