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).
Related
I trying to implements a interface to a controller but when i try that, the request is converted into a string.
Here is the code of the controller:
class FilesController extends Controller implements Repository
{
function __construct()
{
$this->factory = new RepositoryFactoryImp();
}
public function index($request)
{
$repository = $this->factory->createRepository($request->type_repository);
return $repository->getFilesList($request);
}
}
Here is the code of the interface:
interface Repository
{
public function index(GetFileListRequest $request);
}
Then the error that i get is:
ErrorException: Trying to get property 'type_repository' of non-object
in file
C:\xampp\htdocs\pocs\repository\app\Http\Controllers\FilesController.php
on line 31
I do a dd($request); and the result is a string, the string is the content of type_repository variable of the route:
Route::get('files/{type_repository}', 'filesController#index');
What can be the problem? Is possible to implements a interface to a controller?
Well to get started you haven't injected the request in your controller:
class FilesController extends Controller implements Repository
{
// ...
public function index($request) // <-----
{ // ^^^^^^^^^
$repository = $this->factory->createRepository($request->type_repository);
return $repository->getFilesList($request);
}
}
Try doing this instead:
use Illuminate\Http\Request;
// ...
public function index(Request $request) { ... }
// ^^^^^^^^^^^^^^^^
Side note
As an observation, you have declared the index() method in your interface but you are calling the createRepository() one in your implementation.
Here is my model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Associate extends Model
{
// some code
}
In controller I use this model similar this
<?php
namespace App\Http\Controllers;
use App\Models\Associate;
use Illuminate\Http\Request;
class AssociatesController extends Controller
{
protected $associate;
public function __construct(Associate $associate)
{
$this->associate = $associate;
}
public function edit(Request $request, $id)
{
$associate = $this->associate->with('some-relation')->find($id);
// other part of code
}
}
When i wont to testing in controller edit method using phpunit I cant mock with method because it is static method of Illuminate\Database\Eloquent\Model.
My question there is way to delete some method of parent class??
From Laravels documentation
static Builder|Model with(array|string $relations)
Being querying a model with eager loading.
From Php docs
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // Here comes Late Static Bindings
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>
The above example will output:
B
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;
...
...
}
}
I have some trouble with namespace and use.
I get this error: "Trait 'Billing\BillingInterface' not found"
These are the files in my Laravel application:
Billing.php
namespace Billing\BillingInterface;
interface BillingInterface
{
public function charge($data);
public function subscribe($data);
public function cancel($data);
public function resume($data);
}
PaymentController.php
use Billing\BillingInterface;
class PaymentsController extends BaseController
{
use BillingInterface;
public function __construct(BillingPlatform $BillingProvider)
{
$this->BillingProvider = $BillingProvider;
}
}
How to i use use and namespace properly?
BillingInterface is an interface not a trait. Thus it can't find the non existent trait
Also you have an interface called BillingInterface in a namespace called Billing\BillingInterface, the fully qualified name of the interface is: \Billing\BillingInterface\BillingInterface
Perhaps you mean
use Billing\BillingInterface\BillingInterface;
// I am not sure what namespace BillingPlatform is in,
// just assuming it's in Billing.
use Billing\BillingPlatform;
class PaymentsController extends BaseController implements BillingInterface
{
public function __construct(BillingPlatform $BillingProvider)
{
$this->BillingProvider = $BillingProvider;
}
// Implement BillingInterface methods
}
Or to use it as a trait.
namespace Billing;
trait BillingTrait
{
public function charge($data) { /* ... */ }
public function subscribe($data) { /* ... */ }
public function cancel($data) { /* ... */ }
public function resume($data) { /* ... */ }
}
Again the modified PaymentsController, but with fully qualifies names.
class PaymentsController extends BaseController
{
// use the fully qualified name
use \Billing\BillingTrait;
// I am not sure what namespace BillingPlatform is in,
// just assuming it's in billing.
public function __construct(
\Billing\BillingPlatform $BillingProvider
) {
$this->BillingProvider = $BillingProvider;
}
}
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);
}
}