I'm writing a new project with laravel 5.1, and I want to use the repository pattern, but I can't figure out what is the best way of doing that.
I was thinking about:
public function save(User $user)
{
$user->save();
}
public function find($id)
{
return User::find($id);
}
And then
$user = new User();
$user->email = 'foo#bar.com';
$userRepo->save($user);
That way I work with the model as DTO, and it's super easy and comfortable, but then I will be depend on Eloquent.
So I was thinking of using an array instead of model, like that:
public function save(array $user)
{
User::save($user);
}
public function find($id)
{
return User::find($id)->toArray();
}
But then it will be much less comfortable to use.
Can you explain me what is the best way of using repository in Laravel so I will be able to change data source easily in the future, but it will still be comfortable to use this pattern?
I have it set up as follows, using your example:
<?php namespace Repositories;
use Models\User;
class UserEloquentRespository implements UserInterface {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function create($input)
{
$this->user->create($input);
}
}
Then in my controller
Laravel 4
<?php namespace Controllers;
use Repositories\UserInterface as User;
class UsersController extends \BaseController {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function store()
{
$input = \Input::all();
// validation here
// now create your user
$user = $this->user->create($input);
if ($user) {
// redirect or do whatever you do on successful user creations
}
// Here you can do whatever you do if a user wasn't created for whatever reason
}
}
Laravel 5
<?php namespace App\Http\Controllers;
use Repositories\UserInterface as User;
use App\Http\Requests\UserRequestForm;
use App\Http\Controllers\Controller;
class UsersController extends Controller {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function store(UserRequestForm $request)
{
// Form request validates in middleware
// now create your user
$user = $this->user->create($request->input());
if ($user) {
// redirect or do whatever you do on successful user creations
}
// Here you can do whatever you do if a user wasn't created for whatever reason
}
}
Lastly, don't forget to bind your interface to your eloquent repository as a service provider.
App::bind(
'Repositories\UserRepositoryInterface',
'Repositories\Eloquent\UserEloquentRepository'
);
Now, whenever you need to change your implementation just change the binding to whatever new class implements your interface.
Related
I have a simple code that displays to the user all of his notifications received from the Database:
$user_notifications = DB::table('notifications')->where('user_id', $this->user->id)->orderBy('id', 'desc')->get();
the problem is that I have too many controllers and functions in them and I don’t want to duplicate this code everywhere, in each function and controller. How can I make the $user_notifications variable global and use it everywhere?
u need to create a NotificationRepository
class NotificationsRepository
{
private Notifications $model;
public function __construct(Notification $model)
{
$this->model = $model;
}
public function findByUserId(int $userId): Collection
{
return $this->model->where('user_id', $userId)->orderBy('id', 'desc')->get();
}
}
Then in Controller action add this repository by autowiring
class SomeController extends Controller
{
public function someAction(NotificationRepository $repository, int $id)
{
$notifications = $repository->findByUserId($id);
}
}
or this way, i dont know how did u use your actions
class SomeController extends Controller
{
public function someAction(NotificationRepository $repository, Illuminate\Http\Request $request)
{
$notifications = $repository->findByUserId($request->user()->id);
}
}
I try defined property for some controller and i get error :
Symfony \ Component \ Debug \ Exception \ FatalErrorException (E_UNKNOWN)
Constant expression contains invalid operations
This my controller code :
namespace App\Http\Controllers...;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Customer;
use App\City;
use Illuminate\Database\Eloquent\Builder;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
class CatalogController extends Controller
{
protected $user = Auth::user();
function user() {
$user = optional(Auth::user())->load(['city']);
return $user;
}
}
In php.net I read this:
Class member variables may be initialized, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.
I assume that this rule is not executed in my code. Am I right?
Retrieving the user in a controller method is fine because it is only executed once for the request. There is no need to advantage to storing it as instance property because a new instance of the controllers is made for every request.
You could use the following code:
public function index()
{
$user = Auth::user()->with('city')->get();
$articles = $this->getArticlesForUser($user);
return view('home', [
'user' => $user
]);
}
public function getArticlesForUser(User $user){
// Do something with $user
return [];
}
public function delete()
{
$user = Auth::user();
$user->delete();
return view('home');
}
If the goal is to have calculate the values once and use them at various places a singleton can be used:
<?php
class Settings {
private $accessToken = '';
private $items = [];
private static $instance = null;
protected function __construct(){}
public static function getInstance(){
if(static::$instance == null){
static::$instance = static::buildInstance();
}
return static::$instance;
}
private static function buildInstance(){
$instance = new Settings();
$instance->accessToken = md5(rand(1,1000));
$instance->items = [1,2,3];
return $instance;
}
public function getToken(){
return $this->accessToken;
}
}
$settings = Settings::getInstance();
// Both return the same token because the instance is the same
var_dump($settings->getToken());
var_dump(Settings::getInstance()->getToken());
You should assign your properties in constructor like this
protected $user;
public function __construct()
{
$this->user = Auth::user();
}
or through setters...
Edit
However in your case it's not a good idea please see https://laravel.com/docs/5.3/upgrade#5.3-session-in-constructors
I am following this link to implement it
I did below steps to implement the Contract in my existing class.
Below is the class where I will write some logic also before sending it to controller
namespace App\Classes\BusinessLogic\Role;
use App\Classes\DatabaseLayer\Role\RoleDb;
use App\Classes\Contract\Role\IRole;
class RoleBL implements IRole {
public function All() {
return (new RoleDb())->All();
}
}
Database Function
namespace App\Classes\DatabaseLayer\Role;
class RoleDb {
public function All() {
$Roles = \App\Models\Role\RoleModel
::all();
return $Roles;
}
}
Interface
namespace App\Classes\Contract\Role;
interface IRole {
public function All();
}
Service Provider class
namespace App\Providers\Role;
class RoleServiceProvider extends \Illuminate\Support\ServiceProvider {
public function register()
{
$this->app->bind('App\Classes\Contract\Role\IRole', function($app){
return new \App\Classes\BusinessLogic\Role\RoleBL($app['HttpClient']);
});
}
}
Finally in config/app.php in provider wrote below line.
App\Providers\Role\RoleServiceProvider::class
Controller - Constructor
protected $roles;
public function __construct(\App\Classes\Contract\Role\IRole $_roles) {
parent::__construct();
$roles = $_roles;
}
Controller Action method
public function index(IRole $roles) {
$RoleTypes = $roles->All();
}
So far everything works fine if I keep Interface as parameter in method.
if I try to use the variable $roles in index method and remove the variable, it is always null.
Please guide me if I missed anything?
You incorrectly assign the $roles property in your __construct() method.
Replace
$roles = $_roles;
with
$this->roles = $_roles;
and then in your index method do:
$RoleTypes = $this->roles->All();
I am working on a Task Management App. In App there is a User Model and there is Project and Task models.
In order for user to add a Project and related Task, all I would do at Model Level is:
class Project extends Model
{
public function add($user_id,$task_details)
{
//Add Project Meta
$this->title = "Project Title";
$this->description "Desc"
$this->save()
for($t=0;$t<count($task_details);$t++) {
//Add Task Details
$task = new Task();
$task->title = $task_details["title"];
$task->description = $task_details["description"];
$task->save();
}
}
}
It looks clumsy to me. How can I improve this in Laravel 5? How can I make my modules more atomic?
Yeah it is clumsy. You should use Repository pattern so that there is no such type of multiple responsibilities.
And Eloquent already has a create method like findorCreate or just create method.
Here is simple example for UserRepository.
//UserRepository
<?php namespace App\Repository\User;
use App\User;
class UserRepository{
public $model;
public function __construct(User $userModel) // type hinting the User Model
{
$this->model=$userModel;
}
public function create($inputs)
{
return $this->model->create($inputs);
}
}
// UserController
<?php namespace App\Http\Controllers;
use \App\Repository\User\UserRepository;
class UserController extends Controller{
public $userRepo;
public function __construct(UserRepository $userRepository) //type hinting the userRepository Class
{
$this->userRepo=$userRepository;
}
public function getUser($id)
{
return $this->userRepo->model->findorfail($id);
}
public function getCreate()
{
if($this->userRepo->create(Input::all())
return view('success');
return Redirect->back()->withErrors();
}
}
I am new to this concept of DI, and IoC so i might be doing this completely wrong but i am trying to inject the Model that matches a controller into that controllers constructor (UserModel -> UsersController) so that i can mock it later on.
So my model looks like:
use Illuminate\Auth\UserInterface;
class User extends Eloquent implements UserInterface {
public function getAuthIdentifier()
{
return $this->getKey();
}
public function getAuthPassword()
{
return $this->password;
}
}
And i am then trying to inject in UsersController like so :
class UsersController extends Controller {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function index()
{
//dd( $this->user);
$users = $this->user->all();
foreach ( $users as $user )
print_r($user);
return;
}
}
Then when i hit this controller in the browser i get a "Unresolvable dependency resolving" error.
I noticed that this happend only when the class that i am trying to inject is a sub class of eloquent, if i try the same code with a custom class that do not extend eloquent then it works fine.
Am i missing something?
Further to the comments, I finally got to know that this is a complicated issue. To bypass this you need to bind your model with the IoC and return a new instance of your model manually.
App::bind('User', function()
{
return new User;
});