Laravel Eloquent Model Dependency Injection with preformated Id to find() - php

Normally we can simplify finding User by id logic in controller by injecting the User Class in parameter. Like this:
class UserController extends Controller
{
public function show(User $id)
{
return $user;
}
}
But now I must treat the Id to find like this:
<?php
namespace App\Http\Controllers;
class UserController extends Controller
{
public function show(User $id)
{
$preformattedId = '98'.$id;
$user = User::find($preformattedId );
return $user;
}
}
My basic question is: how I can achieved that same trick to my preformatted id in below code like the above code?
Note: I have to use the Id this way because i work with legacy database that actually adding that '98' prefix in every Id, despite that we only use characters after that prefix.

You can use Inversion of Control by using explicit binding on your router.
In your RouteServiceProvider
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return User::find('98'.$value);
});
}
Or in your User model
/**
* Retrieve the model for a bound value.
*
* #param mixed $value
* #param string|null $field
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
return $this->find('98'.$value);
}
https://laravel.com/docs/7.x/routing#explicit-binding

You can share your route file ?
But if your file is
Route::get('user/{id}', 'UserController#show');
When you use
class UserController extends Controller
{
public function show(User $id)
{
// you don't need use find function, it is make automatic by laravel
$user = $id;
return $user;
}
}
if you want to get id, just remove User type inside show parameter
class UserController extends Controller
{
public function show($id)
{
$preformattedId = '98'.$id;
$user = User::find($preformattedId );
return $user;
}
}

Related

Laravel Trait with constructor works in controller but not in model

I have a StripeClient service provider which needs a key to instantiate:-
namespace App\Providers;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use Stripe\StripeClient;
class StripeServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(StripeClient::class, function ($app) {
return new StripeClient(config('services.stripe.secret'));
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return [StripeClient::class];
}
Then a trait with a bunch of api call functions like this:-
trait StripeClientTrait
{
protected $stripe;
function __construct(StripeClient $stripeClient)
{
$this->stripe = $stripeClient;
}
/**
* #param User $user
*
* #return \Stripe\Customer
* #throws \Stripe\Exception\ApiErrorException
*/
function createCustomer(User $user)
{
return $this->stripe->customers->create([ 'name' => $user->fullname,
'email' => $user->email
]);
}
...
The trait works in a controller perfectly as expected:-
class SubscriptionContoller extends Controller
{
use StripeClientTrait;
public function checkout()
{
try {
$customer = $this->createCustomer(Auth::user());
if($checkoutSession = $this->createCheckoutSession($customer)) {
return redirect($checkoutSession->url);
}
} catch (ApiErrorException $ex){
Log::error($ex->getMessage());
return back()->with(['error'=>$ex->getMessage()]);
}
return back();
}
...
But I now need to use the trait in a model to provide access to some api functions.
class Company extends Tenant
{
use HasFactory, StripeClientTrait;
but adding the trait causes:-
Too few arguments to function App\Models\Company::__construct(), 0 passed in /home/vagrant/code/profiler/vendor/spatie/laravel-multitenancy/src/Models/Concerns/UsesTenantModel.php on line 13 and exactly 1 expected
Can anyone tell me how to implement the trait without using the constructor? I just need some static function helpers to lookup stuff on the API.
Thanks for any guidance :-)
having persevered I've found this way to use the service container in a model:-
public function getPrices()
{
$stripe = app(StripeClient::class);
return $stripe->prices->all(['active'=>true]);
}
But would still like to understand how to use the trait in the model, if anyone could explain I'd be grateful

How to use different controllers, depending on the user role, for the same route?

I'm trying to implement multiple controllers which listens to one route /account.
There are two controllers and only one should be executed on that URL where the choice lies within user's role.
namespace AppBundle\Controller;
use AppBundle\Entity\Role;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/account")
*/
abstract class DashboardController extends Controller
{
protected $userRoles;
public function __construct()
{
$this->userRoles = $this->getUser()->getRoles();
}
/**
* Get all user roles
*/
public function getRoles()
{
return $this->userRoles;
}
/**
* Get user account type
*
* #return Role
*/
public function getAccountType(): Role
{
$accountType = new Role();
foreach ($this->userRoles as $role) {
if(Role::ROLE_STUDENT == $role->getName()) {
$accountType = $role;
} else if(Role::ROLE_SCHOOL_REPRESENTATIVE == $role->getName()) {
$accountType = $role;
} else if(Role::ROLE_EMPLOYER == $role->getName()) {
$accountType = $role;
} else if(Role::ROLE_ADMIN == $role->getName()) {
$accountType = $role;
}
}
return $accountType;
}
}
namespace AppBundle\Controller;
class CompanyDashboardController extends DashboardController
{
public function __construct()
{
parent::__construct();
}
/**
* #Route("/", name="dashboard_company_home", methods={"GET"})
* #return Response
*/
public function index()
{
return $this->render('dashboard/company/index.html.twig');
}
}
namespace AppBundle\Controller;
class AdminDashboardController extends DashboardController
{
public function __construct()
{
parent::__construct();
}
/**
* #Route("/", name="dashboard_admin_home", methods={"GET"})
* #return Response
*/
public function index()
{
return $this->render('dashboard/admin/index.html.twig');
}
}
That's what I've got so far.
You can't do this with "route" declarations, since the route listener is executed with higher priority than the security listener. Both happen during the KernelEvents::REQUEST event, but routing comes before firewall.
When the route to controller mapping is being resolved, you do not have yet user information (which is why you can't simply attach another a listener and inject the user information on the Request object, so it's available to use in the route declaration for expression matching, for example).
Basically, one route, one controller. If you want to have diverging logic for these users, you'll have to apply it after you get into the controller.

How to use one model for multiple tables in laravel 5.6

I am looking for solutions, but can't really understand. I'm new in Laravel and I want a simple instruction on how to use one model for multiple tables like CodeIgniter as follows:
Controller myController:
public function shipBuilding()
{
$data = $this->input->post();
$response = $this->MyModel->shipbuildingSave($data);
}
public function contact()
{
$data = $this->input->post();
$response = $this->MyModel->contactSave($data);
}
Model MyModel:
public function shipbuildingSave($data){
$this->db->insert('tbl_shipbuilding', $data);
return $this->db->insert_id();
}
public function contactSave($data){
$this->db->insert('tbl_contact', $data);
return $this->db->insert_id();
}
This is not how models work in Laravel. each model should be a representation of one single table.
You could, however, change the table name on booting the model up:
class Flight extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'example';
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
// Set the $this->table depending on some logic.
}
}
But again, this is probably not recommended for your case.

Silex call controller within controller

When I receive an API request it routes trough the Application.php to the UserController.
The UserController does his thing with the information and I need to call the EmailController, because that is the controller that manages all the emails.
In the EmailController I have a function (its simplified):
class EmailController {
public function getEmail() {
return 1337 ;
}
}
In the UserController I have a function:
class UserController {
public function getUserMail(Request $request, Application $app) {
$number = ???;
return $number;
}
}
What do I have to call within the UserController to get the getEmail function of the EmailController?
If this is not a correct way of doing it, I would love to hear what term I am acutally searching for :)
Edit1:
As #lawrence-cherone pointed out, it should have been in a model.
It was stuck in my head that I had to use the controller for this task.
You could use the dependency injection to share the class that return number.
So your controllers will look like:
class EmailController
{
/**
* #var NumberCalculatorInterface
*/
private $numberCalculator;
/**
* #param NumberCalculatorInterface $numberCalculator
*/
public function __construct(NumberCalculatorInterface $numberCalculator)
{
$this->numberCalculator = $numberCalculator;
}
public function getEmail()
{
return $this->numberCalculator->getNumber();
}
}
and
class UserController
{
/**
* #var NumberCalculatorInterface
*/
private $numberCalculator;
/**
* #param NumberCalculatorInterface $numberCalculator
*/
public function __construct(NumberCalculatorInterface $numberCalculator)
{
$this->numberCalculator = $numberCalculator;
}
public function getUserMail(Request $request, Application $app)
{
$number = $this->numberCalculator->getNumber();
return $number;
}
}
Your class that calculate number or other more complex logic will be
interface NumberCalculatorInterface
{
public function getNumber();
}
class DefaultNumberCalculator implements NumberCalculatorInterface
{
public function getNumber()
{
return 1337;
}
}
Since the number calculation is not a logic proper to your EmailController cause you use the logic in several classes, it make sense to be an external class. You will be able to unit test it properly and to inject in all the classes that need this calculation to be done.
You will be able to declare it as service:
class NumberCalculatorProvider implements ServiceProviderInterface {
public function register(Container $pimple)
{
$pimple['number_calculator'] = function () {
return new DefaultNumberCalculator();
};
}
}
And inject it inside your controller easily (in the following example is use the ServiceControllerServiceProvider to declare controller as services):
class ControllerProvider implements ServiceProviderInterface {
public function register(Container $pimple)
{
$pimple['controller.user'] = function ($pimple) {
return new UserController($pimple['number_calculator']);
};
$pimple['controller.email'] = function ($pimple) {
return new EmailController($pimple['number_calculator']);
};
}
}
note: In my example i use silex 2., since its not specified in your question, you may need to adapt it if you use an older version but the logic remain the same.*
I think you need to make UserController inherit the function getEmail() from EmailController
class UserController extends EmailController {
public function getUserMail(Request $request, Application $app) {
$number = ???;
return $number;
}
}

how to Inject a Sentry 2 User Model into a controller - laravel 4

How do you inject a Sentry 2 User model into a laravel 4 controller using IoC?
for example i would like the following
class myController extends BaseController {
/**
* creates a list of MyModel Models
*
* #return View
*/
public function getIndex( User $user )
{
// fetch models
$models = MyModel::all();
// Show the page
return View::make('my-views.the-view', compact('models', 'user'));
}
}
This is how I like to do it:
class myController extends BaseController {
$protected $user
/**
* creates a list of MyModel Models
*
* #return View
*/
function __construct(User $user){
parent::__construct();
$this->user = $user;
}
public function getIndex()
{
// fetch models
$models = MyModel::all();
// Show the page
return View::make('my-views.the-view', compact('models', 'user'));
}
}
You can also do it in the method, but... well, give this a good read, too: http://fabien.potencier.org/article/11/what-is-dependency-injection

Categories