Laravel: use extended controller or Traits or something else? - laravel

To maintain my Laravel application and save myself from a lot of duplicate code I have made the following solution:
BaseController
class BaseController extends Controller
{
public function get($id){
return $this->baseService->get($id);
}
public function getAll(){
return $this->baseService->getAll();
}
}
BaseService
class BaseService
{
protected $model;
public function __construct($model){
$this->model = $model;
}
public function get($id){
return response()->json($this->model->where('id', $id)->first());
}
public function getAll()
{
return $this->model->get();
}
}
MyController
class MyController extends BaseController
{
protected $model;
protected $baseService;
public function __construct(){
$this->model= new Model();
$this->baseService = new BaseService($this->model);
}
/**
* This controller has all the functionality from BaseController now
*/
}
What I'm wondering if this is a good method. Should I stick with this or should I use a different approach? I've heard about Traits but not sure if they are doing the same thing. It's Laravel 5.5 I'm using.

Yes, traits are used to move methods out of a controller regularly. A good example that the Laravel framework uses is the ThrottlesLogin trait. Take a look at https://github.com/laravel/framework/blob/5.5/src/Illuminate/Foundation/Auth/ThrottlesLogins.php#L20
to see how the methods are moved outside of a controller but can be still accessed by importing the trait using the use keyword.
While traits would work for your use case I wouldn't use them here for the functionality you are looking for. I would use the repository pattern. It would better separate your code and make it more reusable.
Take a look at https://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5/ for more information on the repository pattern. Basically, you would separate your code into a separate repository and use Laravel's built in IoC to inject the repository into your controller.
MyController
class MyController extends Controller
{
protected $repo;
public function __construct(MyRepository $myRepository)
{
$this->repo = $myRepository;
}
public function index()
{
$myStuff = $this->repo->all();
}
// you can also inject the repository directly in the controller
// actions.
// look at https://laravel.com/docs/5.5/controllers#dependency-injection-and-controllers
public function other(MyRepository $repo)
{
$myStuff = $repo->all();
}
}

This is the perfect use case for a Trait. Traits are intended for reusable functions. They're super simple to implement, and won't take more than a few minutes to change what you have.
Here is a great article on them: https://www.conetix.com.au/blog/simple-guide-using-traits-laravel-5

Related

Laravel contextual binding to be more specific to methods rather then class only

I am trying to understand laravel bind.
let's say, I have UploadFileController.php
Route::post('/upload/images', 'UploadFilesController#uploadImage');
Route::post('/upload/pdf', 'UploadFilesController#uploadPdf');
then in the controller,
class UploadFilesController extends Controller
{
private $uploadServiceInterface;
public function __construct(UploadServiceInterface $uploadServiceInterface)
{
$this->uploadServiceInterface = $uploadServiceInterface;
}
public function uploadImage(Request $request)
{
$this->uploadServiceInterface->store();
}
public function uploadPdf()
{
$this->uploadServiceInterface->store();
}
}
Now, the uploadServiceProvider,
class UploadServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->when(UploadFilesController::class)
->needs(UploadServiceInterface::class)
->give(ImagesUploadService::class);
}
}
Now, I know "when" says that UploadFileController class with uploadService interface will give the imageUploadService but is it possible I make it more specific to function in uploadFileController class, like
$this->app->when(uploadFilesController::uploadImage())
->needs(UploadServiceInterface::class)
->give(ImagesUploadService::class);
then it takes to the imagesUploadService class same for pdf upload class.

How to write a custom function in a model?

There is a model data:
class Order extends Model
{
}
How to write a custom method inside the Order class so that it can be called in constructor like this:
Order::myMethod()
Order->myMethod()
Where myMethod is:
public function myMethod() {
return DB::query(<SQL QUERY>);
}
Purpose is to move SQL queries inside model's class, that don't mess this code in controllers.
Rather create a custom function in Model, You can use traits to achieve the desired output.
Please follow either steps:-
https://medium.com/#kshitij206/traits-in-laravel-5db8beffbcc3
https://www.conetix.com.au/blog/simple-guide-using-traits-laravel-5
Guess you are asking about the static functions:
class Order extends Model {
public static function myMethod() {
}
}
and you can call it anywhere like
Order::myMethod();
You can achieve the desired behavior using magic methods __call and __callStatic
if your real method is static you can use __call() to intercept all "non static" calls and use it to call the static and use __callStatic to forward the calls to a new instance to that class .
Your methods should be always static because if a non static method exists and you are calling it statically php raises an error
Non-static method Foo::myMethod() should not be called statically
No problem if your method is static
class Order extends Model {
public static function myMethod() {
return static::query()->where(...)->get(); // example
}
public function __call($name, $arguments) {
return forward_static_call_array([__CLASS__, $name], $arguments);
}
public static function __callStatic($name, $arguments) {
return call_user_func_array([app(__CLASS__), $name], $arguments);
}
}
(new Order())->myMethod();
Order::myMethod();
I can't understand your exact problem is. but if you are using laravel, then you can write custom method inside the ABC model like this
class ABC extends Model
{
//here is your fillable array;
public function abc()
{
//Here is your Eloquent statement or SQL query;
}
}
just call this abc() method inside the controller like this
use ABC;
class AbcController extends Controller
{
private $_abc; // it is private variable
// this is constructor
public function __construct(ABC $abc)
{
$this->_abc= $abc;
}
public function abcMethod()
{
$this->_abc->abc();
}
}
Thanks
I don't believe I'm understanding your intention. You've stated:
Purpose is to move SQL queries inside model's class, that don't mess this code in controllers.
Why does the Order->myMethod() need calling inside the constructor? If you're trying to design your data access layer to work efficiently, you can use data repositories.

Declare value ONCE to use in multiple controller methods - Laravel 5.2

I currently have to declare my Stripe api key in every controller method that will make an api call to Stripe. For example
public function __construct()
{
\Stripe\Stripe::setApiKey(env('STRIPE_KEY'));
}
public function addCard()
{
Stripe::setApiKey(env('STRIPE_KEY'));
}
public function updateCard()
{
Stripe::setApiKey(env('STRIPE_KEY'));
}
public function deleteCard()
{
Stripe::setApiKey(env('STRIPE_KEY'));
}
This is getting very annoying as I have more than 10 methods that are doing this. My question is, is there a way i can declare the key somewhere ONCE and not have to call it in every controller method?
Yep, I'd do it in your AppServiceProvider. You'll find this at app/Providers/AppServiceProvider.php.
Stick it in the register method:
public function register()
{
Stripe::setApiKey(env('STRIPE_KEY'));
}
That will run during bootstrap for every request.
Alternatively you could set this in the constructor for your Controller. This would cause it to run for all actions in this one controller only.
class PaymentController extends Controller {
public function __construct() {
Stripe::setApiKey(env('STRIPE_KEY'));
}
public function addCard() {
...
I'd argue this is less desirable, your service providers should primarily be responsible for wiring up your dependencies. Up to you.

CodeIgniter Has relationship between models

I've been googling on this issue for a quite while and still haven't found a satisfactory answer yet :(
It's a small web-based online game where a player will have a game board and a resource board. What is the best way to include those two classes in the player class to create a has-relationship?
Is there a particular way to do it in CI? or just go with include()?
An alternative is dependancy Injection
class userModel extends CI_Model
{
public function __construct(gameModel $gameModel)
{
var_dump($gameModel instanceof gameModel);
}
}
-
class Controller extends CI_Controller
{
public function method()
{
$this->load->model('gameModel');
// load model with dependancy on gameModel
// or vise-verse
$this->load->model('userModel', new gameModel);
}
}
-
Or like I mention in comments
class userModel extends CI_Model
{
public function method()
{
$gameModel = $this->load->model('gameModel');
}
public function __get($object)
{
$instance =&get_instance();
return $instance->$object;
}
}

Implementing class inheritance in codeigniter

I am confused, again, about implementing OOP in CodeIgniter.
By design, I have two classes, namely Customer and Supplier. Both classes extends a super class I call Institution.
It was not a problem for me when I wrote them using only php (without framework).
class Customer extends Institution {
public function __construct() {
parent::__construct();
}
}
class Supplier extends Institution {
public function __construct() {
parent::__construct();
}
}
class Institution extends DBConnection {
public function __construct() {
parent::__construct();
}
}
class DBConnection {
// do CRUD activities here
}
The questions are:
How do I write them using CI?
Is it as Controller or in Model the best way to implement them? What factors should be considered?
A friend suggested a way I thought a bit hacky, model extends model. I try, if possible, to do it in codeigniter-appropriate way.
Any suggestion will be appreciated.
Well, as it is going to be your domain model entities, it should be models. And there is nothing wrong in having another model class as a base class for model class. So, you'll just need to make your Institution (or DBConnection if you prefer to keep it) class extend CI_Model.

Resources