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!
Related
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());
}
}
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();
}
}
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;
}
}
}
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();
}
}
I'm creating a multi-tenant application and assigning a DB to each user and his/her sub-users. I first fetch the DB name / password / username for this user and then I need to make it the default for all DB operations to follow. How to accomplish this in Laravel 4?
You can use the connection method:
User::connection('master')->where('name', $name)->get();
And you can use the repository pattern to help you with this too:
class PostRepository {
private $connection = 'default-connection';
public function setConnetion($connection)
{
$this->connection = $connection;
}
public function all()
{
return Post::on($this->connection)->all();
}
public function query()
{
return Post::on($this->connection);
}
}
It will let you:
$post = new PostRepository;
$post->setConnection('new-connection');
return $post->query()->where('name', $name)->get();
or
return $post->all();
You can also create a BaseModel and do inside it:
class BaseModel extends Eloquent {
private $connection = 'default-connection';
public function __construct()
{
if (Auth::check())
{
$this->setConnection(Auth::user()->database_connection);
}
}
}
class Post extends BaseModel {
}