I have the following Code:
Controller
class UserController {
public function __construct(userRepository $userRepository) {
$this->userRepository = $userRepository;
}
[...]
Repository
class UserRepository extends AbstractRepository {
public function getTablename() {
return "tbl_users";
}
public function getModel() {
return "administration\\CMR\\UserModel";
}
[...]
AbstractRepository
abstract class AbstractRepository {
protected $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
abstract public function getTablename();
abstract public function getModel();
function readAll() {
$table = $this->getTablename();
$model = $this->getModel();
$stmt = $this->pdo->query("SELECT * FROM $table");
$res = $stmt->fetchAll(PDO::FETCH_CLASS, $model);
return $res;
}
[...]
My Problem is, that I need the "tbl_users" for one query and a secound table (tbl_locations) for another query. Could anyone please explain how to do this? I think it's unnecessary to write again the same readAll()-Function only with other variable.
Start by adding location repository
class LocationRepository extends AbstractRepository implements LocationRepositoryInterface {
public function getTablename() {
return "tbl_locations";
}
public function getModel() {
return "administration\\CMR\\LocationModel";
}
[...]
and put both repositories to upper layer, say UserLocationService.
class UserLocationService implements UserLocationInterface {
public function __construct(
protected UserRepositoryInterface $userRepository,
protected LocationRepositoryInterface $locationRepository
) {
}
[...]
}
This way you have separate repositories for users and locations, yet you can operate on both, in Service that does require both repositories to fulfill its' logic.
If I understand correctly, you want to be able to set the tablename to your likings. One possibility is to pass a tablename in the UserRepository constructor (and set a default), like this:
class UserRepository extends AbstractRepository {
public function getTablename() {
return $this->tablename;
}
public function getModel() {
return "administration\\CMR\\UserModel";
}
[...]
abstract class AbstractRepository {
protected $pdo;
protected $tablename;
public function __construct(PDO $pdo, string $tablename = "tbl_users") {
$this->pdo = $pdo;
$this->tablename = $tablename;
}
abstract public function getTablename();
abstract public function getModel();
function readAll() {
$table = $this->getTablename();
$model = $this->getModel();
$stmt = $this->pdo->query("SELECT * FROM $table");
$res = $stmt->fetchAll(PDO::FETCH_CLASS, $model);
return $res;
}
[...]
Related
I'm new to UnitTest and trying to integrate it into my Laravel application, but I'm getting the below error:
Call to a member function findOne() on null
at app/Services/User/UserService.php:32
28▕ $this->userWebsiteRepository = $userWebsiteRepository;
29▕ }
30▕
31▕ public function findOne($data = []){
➜ 32▕ return $this->userRepository->findOne($data);
33▕ }
34▕
This is my code.
AuthController.php
class AuthController extends Controller {
private $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function show($id){
return $this->userService->findOne(['id' => $id]);
}
}
UserService.php
class UserService
{
public $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
}
UserRepositoryInterface.php
interface UserRepositoryInterface
{
public function findOne($data);
}
UserRepository.php
use App\Models\User;
class UserRepository implements UserRepositoryInterface
{
private $model;
public function __construct(User $user)
{
$this->model = $user;
}
public function findOne($data)
{
if (empty($data)) return false;
$query = $this->model->with(['userWebsites', 'userWebsites.website', 'role']);
if(!empty($data['id'])) $query = $query->where('id', $data['id']);
return $query->first();
}
}
RepositoryServiceProvider.php
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}
}
AuthControllerTest.php
class AuthControllerTest extends TestCase
{
public $authController;
public $userRepositoryInterfaceMockery;
public $userServiceMokery;
public function setUp(): void{
$this->afterApplicationCreated(function (){
$this->userRepositoryInterfaceMockery = Mockery::mock(UserRepositoryInterface::class)->makePartial();
$this->userServiceMokery = Mockery::mock((new UserService(
$this->app->instance(UserRepositoryInterface::class, $this->userRepositoryInterfaceMockery)
))::class)->makePartial();
$this->authController = new AuthController(
$this->app->instance(UserService::class, $this->userServiceMokery)
);
}
}
public function test_abc_function(){
$res = $this->authController->abc(1);
}
}
I was still able to instantiate the AuthController and it ran to the UserService. but it can't get the UserRepositoryInterface argument. I think the problem is that I passed the Interface in the constructor of the UserService. .What happened, please help me, thanks
I don't know where $userService comes from to your controller's constructor, but it seems like it comes from nowhere. You need to pass it as argument, so Laravel can resolve its instance in service container.
class AuthController extends Controller {
private $userService;
public function __construct(
private AuthService $authService,
UserRepositoryInterface $userRepository
) {
$this->userService = new UserService($userRepository);
}
public function show($id)
{
return $this->userService->findOne(['id' => $id]);
}
}
Also there is literally no findOne method in UserService. You need one there.
class UserService
{
public function __construct(private UserRepositoryInterface $userRepository)
{
}
public function findOne(array $data)
{
return $this->userRepository->findOne($data);
}
}
Update
In that case you need this in service provider:
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
$this->app->bind(UserService::class, function ($app) {
return new UserService($app->make(UserRepositoryInterface::class));
});
I have an issue with the way my classes are implemented
I have an abstract class which implements an interface :
interface DriverInterface {
public function __construct(
array $tableGateway,
Sql $sql,
$dbName,
);
public function process();
public function setIdentiteService(IdentiteService $service);
public function setPhotoService(PhotoService $service);
public function setDocumentService(DocumentService $service);
public function setLotService(LotService $lotService);
}
Abstract class:
abstract class AbstractDriverMigration implements DriverInterface {
protected $tableGateway;
protected $sql;
protected $dbName;
public function __construct(array $tableGateway, Sql $sql, $dbName)
{
$this->tableGateway = $tableGateway;
$this->sql = $sql;
$this->dbName = $dbName;
}
abstract public function process();
public function setIdentiteService(IdentiteService $service) {}
public function setPhotoService(PhotoService $service) {}
public function setDocumentService(DocumentService $service) {}
public function setLotService(LotService $service) {}
}
Then I call the classes which extends the abstract class programmatically :
$className = 'Migration\\Driver\\' . str_replace(' ', '', ucwords(str_replace('_', ' ', $class)));
$driver = new $className(...);
But the thing is that, for some classes, but not all, I need to pass services with the 'setXService' functions that I override from the abstract class, and I end up testing which class it is to pass the services. And I don't like doing that.
if ($class == 'user' ) {
$driver->setLotService($service)
}
And I don't want to pass the services to all the classes, since some of them don't need them.
I'm trying to learn and implement the Repository Pattern in my app built with Laravel 5.6.
I have implemented my Controller:
class CompaniesController extends Controller
{
protected $company;
public function __construct(ICompanyRepository $company) {
$this->company = $company;
}
public function index(){
$companies = $this->company->getAllCompanies();
return view('companies::index')->with("companies", $companies);
}
}
Then I have implemented the repository interface:
interface ICompanyRepository
{
public function getAllCompanies();
public function findBy($att, $columns);
public function getById($id);
public function with($relations);
}
I have implemented My Repository:
class CompaniesRepository implements ICompanyRepository
{
protected $model;
public function __construct(Companies $model){
$this->model = $model;
}
public function getAllCompanies(){
return $this->model->all();
}
public function findBy($att, $columns)
{
return $this->model->where($att, $columns);
}
public function getById($id)
{
return $this->model->findOrFail($id);
}
public function with($relations)
{
return $this->model->with($relations);
}
}
And then I have created the model:
class Companies extends Model
{
protected $fillable = [];
protected $casts = [
'settings' => 'array'
];
//my question is here!
public function members(){
return $this->hasMany('Companies\Entities\CompaniesMembers');
}
}
For now I have put the relations (in this case members function) in the model, but with this way, If I had to change my ORM, I should change both repository and model, because for now I use Eloquent, but I don't know if in future I will use Doctrine or others.
So my question is:
Where is the best place the relationships and the functions for db?
Is right put all in the Model or It would be better put all in Repository?
So in the EloquentRepo you make a function that combines company with companymembers by a foreach and use modelToObject($model) Something like this. I hope this will help you the good direction.
EloquentRepo:
private function modelToObject($model)
{
if (is_null($model)) {
$entity = null;
} else {
$entity = new Product(
$model->{Model::COL_ID},
$model->{Model::COL_NAME}
);
}
return $entity;
}
Entity:
class Product{
private $id;
private $name;
public function __construct(int $id, string $name) {
$this->setId($id)
->setName($name);
}
public function setName(string $name): Product{
$this->name = $name;
return $this;
}
public function getName(): string {
return $this->name;
}
}
I need to render polymorphic models in different situations.
Model's base class:
abstract class BaseDiscount {
abstract public function createRenderer(IDiscountRendererFactory $factory);
}
IDiscountRendererFactory:
interface IDiscountRendererFactory {
/**
* #return IDiscountRenderer
*/
public function createDiscountRenderer(Discount $model);
/**
* #return IDiscountRenderer
*/
public function createActionRenderer(Action $model);
}
Discount model class:
class Discount extends BaseDiscount {
public function createRenderer(IDiscountRendererFactory $factory) {
return $factory->createDiscountRenderer($this);
}
}
Action model class:
class Action extends BaseDiscount {
public function createRenderer(IDiscountRendererFactory $factory) {
return $factory->createActionRenderer($this);
}
}
IDiscountRenderer:
interface IDiscountRenderer {
public function render();
}
In the client module I have:
class ConcreteDiscountRenderer implements IDiscountRenderer {
public function __construct(Discount $model) {
//do something
}
public function render() {
//do something
}
}
class ConcreteActionRenderer implements IDiscountRenderer {
public function __construct(Action $model) {
//do something
}
public function render() {
//do something
}
}
class ConcreateDiscountRendererFactory implements IDiscountRendererFactory {
public function createDiscountRenderer(Discount $model) {
return new ConcreteDiscountRenderer($model);
}
public function createActionRenderer(Action $model) {
return new ConcreteActionRenderer($model);
}
}
$repository = new DiscountRepository();
/** #var BaseDiscount[] $discounts */
$discounts = $repository->findAll();
$factory = new ConcreateDiscountRendererFactory();
foreach ($discounts as $discount) {
$discount->createRenderer();
$renderer = $discount->createRenderer($factory);
$renderer->render();
}
In other application parts may be other implementatations.
I think I got some combination of Visitor and AbstractFactory patterns.
Is it a right approach or is there a better solution?
UPD
If I add new model class, like DiscountProgramm extends BaseDiscout, I have to refactor IDiscountFactoryInterface and all it's realizations. Is there an approach which allows to avoid that?
Warning: might cause TL:DR
I am working with PHP 5.3.10 and have the following problem. I do have an abstract class DataMapper, which is extended for the specific DataModel I want to persist. The following code does this trick:
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public abstract function save(IModel $model); // DISCUSSION
/* more helper functions here */
}
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic ... */ }
public function fetchAll() { /* ...magic ... */ }
public function save(IModel $model) { /* ...magic ... */ } // DISCUSSION
}
interface IModel {
public function setOptions(array $options);
public function toArray();
}
abstract class Model implements IModel {
protected $_fields = array();
protected $_data = array();
public function setOptions(array $options) { /* ...magic ... */ }
public function toArray() { /* ...magic ... */ }
public function __construct(array $options = null) { /* ...magic ... */ }
public function __set($name, $value) { /* ...magic ... */ }
public function __get($name) { /* ...magic ... */ }
}
class PersonModel extends Model {
protected $_fields = array('id', 'name', 'passhash', /*...*/);
public function setId($value) {
/* ...Validation happening... */
$this->_data['id'] = $value;
return $this;
}
public function checkPassword($password) { /* ...magic... */ }
}
This works fine, but is really quirky for my feeling.
As you can see, I've used an interface IModel to be able to tell the DataMapper, that it does need a certain set of parameters and methods. However, some Models do have extra methods needed by the corresponding DataMapper - in the example, a checkPassword() method, which is used test a password against the stored hash value. This method may also instruct the DataMapper to rehash the just tested password and update it due to new requirements (e.g. an increased difficulty for a password hash function).
So what I actually want is to change the signature of PersonMapper to PersonMapper::save(PersonModel $model) - and e.g. in another DataMapper toPostMapper::save(PostModel $model), etc. This is due to these DataMappers needing a certain signature. So my ideal solution looks like this:
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public abstract function save(Model $model); // UPDATED
}
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(PersonModel $model) { /* ...magic... */ } // UPDATED
}
abstract class Model { /* ...unchanged... */ }
class PersonModel extends Model { /* ...unchanged... */ }
Notice the Update save-Methods in the abstract class and its implementation. Since PersonModel is inherited from Model, thus obviously having a common base set of signatures, I would expect this to work just fine. But it doesn't - PHP complains about a changed interface in the childclass PersonMapper
My Questions:
Is there another solution working with PHP 5.3.10 that expresses the relationship better?
Does it work in a later version of PHP, so that it might be worth upgrading the server?
You might try using interfaces instead.
interface OtherModel {
public function getThis();
}
interface OtherOtherModel {
public function getThat();
}
Your Model Class might implement one or more interfaces...
class PersonModel extends Model implements OtherModel {
protected $_fields = array('id', 'name', 'passhash', /*...*/);
public function setId($value) {
/* ...Validation happening... */
$this->_data['id'] = $value;
return $this;
}
public function checkPassword($password) { /* ...magic... */ }
public function getThis() {
// ...
}
}
Your concrete Mapper Class can use the instanceof to check if this Model does what it should.
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(Model $model) {
// verify that certain methods are implemented...
// throw an exception or reacting accordingly
print ($model instanceof PersonModel)? 'yes' : 'no';
print ($model instanceof OtherOtherModel)? 'yes' : 'no';
}
}
Another possible approach might be the following:
<?php
abstract class DataMapper {
public abstract function findById($id);
public abstract function fetchAll();
public function save(Model $model) {
throw new Exception('You have to implement this!');
}
}
Throw an Exception if the save method is not overriden in an inheriting class.
Now you can really use a different typehint.
This will work:
class PersonMapper extends DataMapper {
public function findById($id) { /* ...magic... */ }
public function fetchAll() { /* ...magic... */ }
public function save(PersonModel $model) {
// do something
}
}
I could think of another possible approach, by using interfaces to define the implementation.
Like for example:
interface PersonModelAware {
public function save(PersonModel $model);
}
interface OtherModelAware {
public function save(OtherModel $model);
}
etc. Your abstract method might have a default save method or no save method at all. The inheriting class will implement the interface it needs.
To sum it up, making your type more specific will not work as the abstract method clearly states it expects a Model.