Where put eloquent relationship with Repository Pattern in Laravel - php

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

Related

Laravel UnitTest not working when pass constructor has Interface

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

Calling multiple methods of the same class in Laravel

i was always curious how does Laravel's eloquent work and how they pass many methods in the same line of code.
Example: auth()->user()->where(...)->get()
In this example they use both methods "where" and "get"
So i tried creating the following repository:
class SkillKeyRepository
{
protected $model;
/**
* #param SkillKey $model
*/
public function __construct(SkillKey $model)
{
$this->model = $model;
}
public function all(array $column = []): Collection
{
return $this->model::all($column);
}
public function where($column, $attribute): SkillKeyRepository
{
$this->model::where($column, $attribute);
return $this;
}
public function get()
{
return $this->model::get();
}
}
After that in my service i tried the following:
class SkillKeyService
{
use ServiceTrait;
protected $repository;
/**
* #param SkillKeyRepository $repository
*/
public function __construct(SkillKeyRepository $repository)
{
$this->repository = $repository;
}
public function get()
{
dd($this->repository->where('key', 'TagName')->get());
}
The correct result should be a collection of one item. But it's returning all the data from the database and its ignoring the where() funcion and going directly to the get() function.
Problem is you use the model, to create a query and then trying to chain more to the query using the original model add newQuery method
class SkillKeyRepository
{
protected $query;
/**
* #param SkillKey $model
*/
public function __construct(SkillKey $model)
{
$this->query = $model->newQuery();
}
public function all(array $column = []): Collection
{
return $this->query::all($column);
}
public function where($column, $attribute): SkillKeyRepository
{
$this->query::where($column, $attribute);
return $this;
}
public function get()
{
return $this->query::get();
}
}
The problem is you are using the model, to create a query and then trying to chain more to the query using the original model, however this won't work because you need the previously generated query.
The easiest way to do what you want is to actually return the query builder object itself:
class SkillKeyRepository
{
protected $model;
/**
* #param SkillKey $model
*/
public function __construct(SkillKey $model)
{
$this->model = $model;
}
public function all(array $column = []): Collection
{
return $this->model->all($column);
}
public function where($column, $attribute): QueryBuilder
{
return $this->model->newQuery()->where($column, $attribute);
}
}
(new SkillKeyRepository(new SkillKey()))->where('a','b')->get(); // Should work
However you should really take a step back and reconsider what you are doing. Laravel already has the query builder object to do exactly this, why are you trying to re-write it?

How to create view/renderer for the polymorphic model

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?

Fatfree framework - create instance of class in another class construct method

404 error while executing code below. The Class 'MagazineModel' extends Controller like Class 'Magazine'. Qestion is: Cant i create object of 'MagazineModel' in Class 'Magazine'?
If i cant, how can i do that in different way? Thanks for help!
Class Magazine extends Controller
{
public $id;
public $name;
public $logo_path;
public $order;
public $description;
//model instance container
public $magazine_model;
public function __construct()
{
parent::__construct();
//properties from model
if (class_exists('MagazineModel')) {
$this->magazine_model = new MagazineModel();
}
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getLogoPath()
{
return $this->logo_path;
}
public function getOrder()
{
return $this->order;
}
public function getDescription()
{
return $this->description;
}
public function testShow()
{
echo $this->twig->render('index.html.twig');
}
}

dynamic query with laravel 4

hello friends need a hand with this query, failed to make it work, I'm new with laravel, I have 3 models:
DiscussCategory
class DiscussCategory extends Eloquent {
protected $table = 'discuss_category';
protected $guarded = array('id');
public function status() {
return $this->belongsTo('Status');
}
public function discuss() {
return $this->hasMany('Discuss');
}
}
Discuss
class Discuss extends Eloquent {
protected $table = 'discuss';
protected $guarded = array('id');
public function discussReplies() {
return $this->hasMany('DiscussReplies');
}
public function discussCategory() {
return $this->belongsTo('DiscussCategory');
}
public function users() {
return $this->belongsTo('Users');
}
}
Users
class Users extends Eloquent {
protected $table = 'users';
protected $guarded = array('id');
protected $hidden = array('password');
public function getAuthIdentifier() {
return $this->getKey();
}
public function getAuthPassword() {
return $this->password;
}
public function getReminderEmail() {
return $this->email;
}
public function discussReplies() {
return $this->hasOne('DiscussReplies');
}
public function discuss() {
return $this->hasMany('Discuss');
}
}
and this how I try to show the query in view
#foreach($data as $category)
{{$category->discuss->last()->users->nickname}}
#endforeach
if you run a foreach manages to get users, but I just want the name of the last user to post a discuss
Trying to get property of non-object
appreciate a hand, insurance is not much, but I have little experience
In your Discuss model change following method:
public function users() {
return $this->belongsTo('Users');
}
To this (use user not users)
public function user() {
// Assumed one discuss belongs to one user
return $this->belongsTo('Users'); // Rename Users to User and use User here
}
Also you should use singular name for all of your models for example, use User instead of Users.

Categories