Use Factory Pattern in Laravel - laravel

I am working on a Laravel project and want to implement Factory and Repository pattern in my project. However, I still don't know where should I use Factory pattern? It's still very confusing. I understand that it's not necessary to use the Factory pattern for every single project but as a part of my study, I want to learn more about the combination of this 2 patterns. This is my code example. Hope I can get help and explains from you guys. Thank you.
class ProductFactory
{
public function create()
{
return new Product;
}
}
class ProductRepository implements ProductRepositoryInterface
{
protected $productFactory;
public function __contruct(
ProductFactory $productFactory
)
{
$this->productFactory = $productFactory
}
public function all()
{
return Product::all();
}
public function loadById($id);
{
return Product::find($id);
}
public function save($data,$id=NULL)
{
if($id != NULL){
$product = $this->loadById($id)
}
else{
$product = $this->productFactory->create();
}
return $product->fill($data)->save();
}
.....
}

I think you need to split the factory and repository. For example:
class ProductRepository implements ProductRepositoryInterface
{
protected $product;
public function __contruct(Product $product)
{
$this->product = $product;
}
//...
}
But not necessary for DI in repository, your methods like all(), find() and etc. put in AbstractRepository. Your ProductRepositry:
class ProductRepository extends AbstractRepository implements ProductRepositoryInterface
{
public function __contruct()
{
//model property in AbstractRepository
$this->model = new Product();
}
}
If you want to write test for it, you may use laravel container:
AppServiceProvider:
$this->app->bind(Product::class, function ($app) {
return new Product();
});
ProductRepository:
$this->model = app()->make(Product::class);
PS: i think factory useless, in most cases i use container. But in some difficult cases i use factory, for example:
class ProductFactory
{
public static function create(int $type, array $attributes): ProductInterface
{
switch ($type) {
case 'market':
return new MarketProductModel($attributes);
break;
case 'shop':
$model = new ShopProductModel($attributes);
$model->setMathCoef(1.2);
return $model;
break;
case 'something':
return new SomethingProductModel($attributes);
break;
}
}
}

Related

Singleton Service does not work as expected

The singleton instance that injected through the service provider is not remain between Requests, which it should. Maybe it's because of my implementation, so can you help me check it:
I has a interface & a class that implements it:
namespace App\Interfaces\CategoryRepositoryInterface;
interface CategoryRepositoryInterface
{
public function testSingleton($value = null);
}
......
namespace App\Repositories
use App\Interfaces\CategoryRepositoryInterface;
class CategoryRepository implements CategoryRepositoryInterface
{
private ?string $value = null;
public function testSingleton($value = null)
{
if ($value != null) {
$this->value = $value;
}
return $this->value;
}
}
Then I bind it with the service provider, like this:
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(
CategoryRepositoryInterface::class,
CategoryRepository::class
);
}
Then I injects it in the controller action. Take a look at the detail() function, when I redirected it from list(), the value that set to the $catRepo was lost because it value is Null at this point.
class CategoryController extends Controller
{
public function list(CategoryRepositoryInterface $catRepo, Category $cat) {
$catRepo->testSingleton('value is set');
$check = $cat->repo->testSingleton();
return to_route('category.detail');
}
public function detail(CategoryRepositoryInterface $catRepo) {
$check = $catRepo->testSingleton();
}
}
So, if there is nothing wrong with how the singleton to work this way, what is the difference between singleton and scoped?
Thank you very much guys for helping me!

How can I avoid repeating this line in all my methods?

I am working on a blogging application in Laravel 8.
I have a settings table from which I pull the directory name of the current theme.
class ArticlesController extends Controller {
public $theme_directory;
public function index() {
// Theme _directory
$this->theme_directory = Settings::all()[0]->theme_directory;
// All articles
$articles = Article::all();
return view('themes/' . $this->theme_directory . '/templates/index', ['articles' => $articles]);
}
public function show($slug) {
// Theme _directory
$this->theme_directory = Settings::all()[0]->theme_directory;
// Single article
$article = Article::where('slug', $slug)->first();
return view('themes/' . $this->theme_directory . '/templates/single', ['article' => $article]);
}
}
The problem
A you can see, the line $this->theme_directory = Settings::all()[0]->theme_directory is repeted in both methods (and would be repeted in others in the same way).
Question
How can I avoid this repetition (and make my code DRY)?
Inheritance approach
Inheritance for a controller would avoid you from repeating it.
abstract class CmsController extends Controller{
protected $themeDirectory;
public function __construct()
{
$this->themeDirectory= Settings::first()->theme_directory ?? null;
}
}
Extend it and you can access it like you have always done.
class ArticlesController extends CmsController
{
public function index() {
dd($this->themeDirectory);
}
}
Trait
Use traits which is partial classes, done by just fetching it, as it is used in different controllers the performance is similar to saving it to an property as it is never reused.
trait Themeable
{
public function getThemeDirectory()
{
return Settings::first()->theme_directory ?? null;
}
}
class ArticlesController extends CmsController
{
use Themeable;
public function index() {
dd($this->getThemeDirectory());
}
}
Static function on model
If your models does not contain to much logic, a static function on models could also be a solution.
class Setting extends model
{
public static function themeDirectory()
{
return static::first()->theme_directory ?? null;
}
}
class ArticlesController extends CmsController
{
use Themeable;
public function index() {
dd(Setting::themeDirectory());
}
}

Laravel: How to implement Repository Design Pattern with only one repository?

I've checked many repository design pattern tutorials like
https://asperbrothers.com/blog/implement-repository-pattern-in-laravel/
https://www.larashout.com/how-to-use-repository-pattern-in-laravel
https://laravelarticle.com/repository-design-pattern-in-laravel
https://shishirthedev.medium.com/repository-design-pattern-in-laravel-application-f474798f53ec
But all use multiple repositories with all methods repeated for each model here's an example
class PostRepository implements PostRepositoryInterface
{
public function get($post_id)
{
return Post::find($post_id);
}
public function all()
{
return Post::all();
}
}
interface PostRepositoryInterface
{
public function get($post_id);
public function all();
}
class PostController extends Controller
{
protected $post;
public function __construct(PostRepositoryInterface $post)
{
$this->post = $post;
}
public function index()
{
$data = [
'posts' => $this->post->all()
];
return $data;
}
}
In ReposiroryServiceProvider:
$this->app->bind(
'App\Repositories\PostRepositoryInterface',
'App\Repositories\PostRepository'
);
So now I have UserRepository, PostRepository, CommentRepository .... etc I will have to add the same methods of get, add, .... in all repositories and just change the model name from Post to User .... etc
How can I unify these methods in one file and just pass the model name and use it like this $this->model->all() instead of repeating them in every repository file I create?
You need Abstract Class AbstractRepository, something like this.
Btw, maybe you don't need repository pattern, in Laravel it is not best practice.
abstract class AbstractRepository
{
private $model = null;
//Model::class
abstract public function model(): string
protected function query()
{
if(!$this->model){
$this->model = app($this->model());
}
return $this->model->newQuery()
}
public function all()
{
return $this->query()->all();
}
}

Laravel test Mockery

Laravel: 6.x
PHP 7.3
I'am wirte a Repositorytest. below is my code, how can I fix this error,
error
TypeError: Return value of Mockery_5_App_Repositories_PersonRepository::fetchOrNewPersonById() must be an instance of App\Models\PersonModel, instance of Mockery\CompositeExpectation returned
Repository
class ContactRepository implements ContactRepositoryInterface
{
private $contact;
private $personRepo;
public function __construct(Contact $contact)
{
$this->contact= $contact;
$this->personRepo= app()->make(PersonRepositoryInterface::class);;
}
public function updateContacts(array $datas): bool
{
foreach ($datas as $key => $value) {
$person = $this->personRepo->fetchOrNewPersonById($value['person_id']);
$person->fill($value);
if( !$person->save()){ return false; }
}
return true;
}
}
and
class PersonRepository implements PersonRepositoryInterface
{
private $personModel;
public function __construct(Person $personModel)
{
$this->personModel= $personModel;
}
public function fetchOrNewPersonById($id): PersonModel
{
return $this->personModel->firstOrNew(['id' => $id]);
}
}
..which implements a Model.
class PersonModel extends Model
{
protected $table = 'person';
}
and
class Contact extends Model
{
protected $table = 'contacts';
}
my testing code
class ContactRepositoryTest extends \Myname\Myapptests\TestCase
{
/**
* #test
*/
public function test_can_update()
{
$personMock = Mockery::mock(PersonModel::class)
->shouldReceive('fill')
->shouldReceive('save')
->andReturn(true);
$this->app->instance(PersonModel::class, $personMock);
$personRepoMock = Mockery::mock(PersonRepository::class)
->shouldReceive('fetchOrNewPersonById')->andReturn($personMock)
->getMock();
$this->app->bind(ContractRepositoryInterface::class, function () use ($contractRepoMock) {
return $contractRepoMock;
});
$modelMock = Mockery::mock(Contact::class)
$service = new class($modelMock) extends ContactRepository {};
$service->updateContacts([
['person_id' => 3, 'address' => ' XXXXXX update address'],
['person_id' => null, 'address' => ' XXXXXX create address'],
]);
$this->assertTrue(true);
}
}
Obviously I am missing something. Other examples explicitly inject the mocked object into the class they are then testing. Laravels IoC is (should be) doing this for me. Do I have to bind anything?

Laravel - check if the Eloquent object is found inside model

In my controller I would like to have this:
class TodoController extends Controller {
public function getDone($todoId)
{
Todo::find($todoId)->markAsDone();
}
}
model I have:
class Todo extends Eloquent {
public function markAsDone()
{
if (???) {
$this->is_done = 1;
$this->save();
}
}
}
How can I check if the model is found and is present in $this?
In your case, checking the existence of ToDo object can only be done in controller. Because in your current code Todo::find($todoId)->markAsDone(); if the $todoId is invalid, you will be have BIG error, trying to get property of non-object. So its better to do this.
class TodoController extends Controller {
public function getDone($todoId)
{
$todo = Todo::find($todoId);
if ($todo) {
$todo->markAsDone();
}
}
}
And in your model
class Todo extends Eloquent {
public function markAsDone()
{
$this->is_done = 1;
$this->save();
}
}

Resources