Laravel 4 dynamic model call - php

I'm developing an API to acces some data on my database. I'm creating a controller for each part of the API. For example, I will have a controller to attend API calls to get a film list (FilmsController) and other controller to attend API calls to get a director list (DirectorsController)
Each controller will have a basic set of methods (getList, getInfo) so I made an ApiController to use as the base for the others. In the ApiController I have the basic set of methods but I have to call the models in non very polite way.
I'm I missing something? Is there any other way to call the models dynamically? I'm using the controllers wrong?
Here is the code, thanks.
class ApiController extends BaseController {
protected $model = '';
public function getList()
{
$items = call_user_func(array($this->model,'all'));
return Response::json($items);
}
...
}
And the FilmsController
class FilmsController extends ApiController {
protected $model = 'Film';
}
Am I going with a bad design?

If you really want to bind model to controller, it would be better to use Laravel IoC container and its automatic resolution feature.
class ApiController extends BaseController {
protected $model;
public function getList()
{
$items = $this->model->all();
return Response::json($items);
}
}
class FilmsController extends ApiController {
public function __construct(Model $model)
{
$this->model = $model;
}
}
Find more about this in documentation

why you call model using variable and use call_user_func function
you can just create ApiController as abstract class and you override the basic set of methods (getList, getInfo) into FilmsController and DirectorsController then you can use Film Model
ApiController:
class ApiController extends BaseController {
public function getList()
{
}
FilmsController:
class FilmsController extends ApiController {
public function getList()
{
$items = Film::all();
return Response::json($items);
}
}

Related

How to correctly extend and use other interfaces?

I'm trying to make use of a base interface for all my other interfaces as follows:
Base interface
<?php
namespace App\Repositories\Data;
interface IDataRepository
{
public function getAll();
public function getById($id);
public function create($model);
public function update($model);
public function delete($id);
}
Implemented base interface
<?php namespace App\Repositories\Data;
use Illuminate\Database\Eloquent\Model;
class DataRepository implements IDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
The interface where i'm trying to make use of the base interface
<?php
namespace App\Repositories;
use App\Repositories\Data\IDataRepository;
interface ITestRepository extends IDataRepository
{
}
implementation
<?php namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
}
In my controller i'm trying to just call test repository so i can use all the base repository functions:
class TestController extends Controller
{
protected $testRepository;
public function __construct(Test $test)
{
$this->testRepository = new TestRepository($test);
}
public function index()
{
$data['testData'] = $this->testRepository->getAll();
return view('test', $data);
}
}
But i get the following error:
Class App\Repositories\TestRepository contains 5 abstract methods and
must therefore be declared abstract or implement the remaining methods
My application works fine if i only make use of my base interface and pass through a model. What would be the correct way to share functions from my base interface across all my other interfaces, so as to prevent code duplication? I appreciate any help.
I think that a Trait which will contains all methods of your interface declaration is the best choice. Something like (not sure about logic):
namespace App\Repositories;
trait TDataRepository
{
// model property on class instances
protected $model;
// Constructor to bind model to repo
public function __construct(Model $model)
{
$this->model = $model;
}
// Get all instances of model
public function getAll()
{
return $this->model->all();
}
// create a new record in the database
public function create($model)
{
return $this->model->create($model);
}
// update record in the database
public function update($model)
{
$record = $this->find($model.id);
return $record->update($model);
}
// remove record from the database
public function delete($id)
{
return $this->model->destroy($id);
}
// show the record with the given id
public function getById($id)
{
return $this->model-findOrFail($id);
}
}
And then just use it for classes with base interface:
namespace App\Repositories;
use App\Library\Classes\Test;
use Illuminate\Database\Eloquent\Model;
class TestRepository implements ITestRepository
{
use TDataRepository;
}
Also there are some other options:
abstract class with methods for base interface but it not so flexible like trait,
composition but you should change base idea and create a new entity for composition.
<?php
namespace App\Repositories;
use App\Interfaces\ITestRepository;
class TestRepository implements ITestRepository
{
public function getAll()
{
// TODO: Implement getAll() method.
}
public function getById($id)
{
// TODO: Implement getById() method.
}
public function create($model)
{
// TODO: Implement create() method.
}
public function update($model)
{
// TODO: Implement update() method.
}
public function delete($id)
{
// TODO: Implement delete() method.
}
}
Class must be declared abstract or implement methods 'getAll', 'getById', 'update', 'create', 'delete'
So All the method is by default abstract method in interface and you have to define all method in this class.
The class TestRepository should not implement any interface, but extend DataRepository:
<?php namespace App\Repositories;
use App\Repositories\Data\DataRepository;
class TestRepository extends DataRepository
{
}
DataRepository contains already the implementation of the interface IDataRepository. When you create a class implementing ITestRepository you will have to define the implementation of all the methods in the interface (which are the same as the base interface, in your case).

How Can I Use Global Scope in Laravel Query Builder? (No Eloquent Model)

I'm working with too many mysql large views. I don't want to use Eloquent Model for the views.
I created "ViewBalance extends Illuminate\Support\Facades\DB". Everything worked as I wanted.
But i need to set init() method for company scope.
How can I use the global scope without init() method?
ViewModel
<?php
namespace App\Models\Views;
use App\Facades\CoreService;
use Illuminate\Support\Facades\DB;
class ViewBalance extends DB
{
const COMPANY_COLUMN = 'company_id';
const TABLE = 'view_balances';
public static function init()
{
return parent::table(self::COMPANY_COLUMN)
->where(self::COMPANY_COLUMN, CoreService::companyId());
}
}
In Controller
<?php
$data = ViewBalance::init()->get(); // Worked!
I have answered my own question. Because, I don't want to edit my question for more complicate. I want to talk about a solution to this problem.
I added $table_view variable and getView() method in Laravel model. If you want, you can create trait for clean codes.
It can be accessed easily views. Also it is part of the main model.
For example;
Laravel Basic Account Model
class Account extends Model {
protected $table = 'accounts';
protected $table_view = 'view_accounts';
public function getView()
{
return \DB::table($this->table_view)->where('global_scope', 1);
}
}
Laravel Account Controller
class AccountController extends Controller {
public function index()
{
$items = (new Account)->getView()->paginate(20);
}
}
public function scopeActive($query)
{
return $query->where('active', true);
}
or
public function scopeInactive($query)
{
return $query->where('active', false);
}

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;
...
...
}
}

Yii controller inheritance ( in submodule )

My question is, how can I inherit Controllers action in YII, like:
class MainController extends FController
{
public function ActionIndex()
{
$this->render("index"); //the view;
}
}
--------------------------------------------
class SecondController extends FController
{
public function ActiondIndex()
{
MainController::ActionIndex();
}
}
Actually in my case the SecondController is the DefaultController of a sub-module. I want to make single code based webpage.
Since PHP does not support multiple inheritance, you may access the base class via the parent keyword.
class MainController extends FController
{
public function ActionIndex()
{
$this->render("index"); //the view;
}
}
class SecondController extends FController
{
public function ActionIndex() // Note: you mistyped the name of this action in your example
{
parent::ActionIndex();
}
}
Although you cannot reach the grandparent's ActionIndex method directly, you have to create a workaround for that.

Laravel: Load method in another controller without changing the url

I have this route: Route::controller('/', 'PearsController'); Is it possible in Laravel to get the PearsController to load a method from another controller so the URL doesn't change?
For example:
// route:
Route::controller('/', 'PearsController');
// controllers
class PearsController extends BaseController {
public function getAbc() {
// How do I load ApplesController#getSomething so I can split up
// my methods without changing the url? (retains domain.com/abc)
}
}
class ApplesController extends BaseController {
public function getSomething() {
echo 'It works!'
}
}
You can use (L3 only)
Controller::call('ApplesController#getSomething');
In L4 you can use
$request = Request::create('/apples', 'GET', array());
return Route::dispatch($request)->getContent();
In this case, you have to define a route for ApplesController, something like this
Route::get('/apples', 'ApplesController#getSomething'); // in routes.php
In the array() you can pass arguments if required.
( by neto in Call a controller in Laravel 4 )
Use IoC...
App::make($controller)->{$action}();
Eg:
App::make('HomeController')->getIndex();
and you may also give params
App::make('HomeController')->getIndex($params);
You should not. In MVC, controllers should not 'talk' to each other, if they have to share 'data' they should do it using a model, wich is the type of class responsible for data sharing in your app. Look:
// route:
Route::controller('/', 'PearsController');
// controllers
class PearsController extends BaseController {
public function getAbc()
{
$something = new MySomethingModel;
$this->commonFunction();
echo $something->getSomething();
}
}
class ApplesController extends BaseController {
public function showSomething()
{
$something = new MySomethingModel;
$this->commonFunction();
echo $something->getSomething();
}
}
class MySomethingModel {
public function getSomething()
{
return 'It works!';
}
}
EDIT
What you can do instead is to use BaseController to create common functions to be shared by all your controllers. Take a look at commonFunction in BaseController and how it's used in the two controllers.
abstract class BaseController extends Controller {
public function commonFunction()
{
// will do common things
}
}
class PearsController extends BaseController {
public function getAbc()
{
return $this->commonFunction();
}
}
class ApplesController extends BaseController {
public function showSomething()
{
return $this->commonFunction();
}
}
if you were in AbcdController and trying to access method public function test() which exists in OtherController you could just do:
$getTests = (new OtherController)->test();
This should work in L5.1

Categories