I used the following tutorial to get an idea about interfaces:
http://vegibit.com/what-is-a-laravel-interface/
But I wanted to change the directory of where I am putting my interfaces to "App/Models/Interfaces". And so I did. But now I cannot get it to work anymore. Here is my code:
Routes.php
App::bind('CarInterface', 'Subaru');
Route::get('subaru', function()
{
$car = App::make('CarInterface');
$car->start();
$car->gas();
$car->brake();
});
Model Subaru.php
<?php
use App\Models\Interfaces\CarInterface;
class Subaru implements CarInterface {
..etc
Interface CarInterface
<?php namespace App\Models\Interfaces;
interface CarInterface {
public function start();
public function gas();
public function brake();
}
I added this in my composer.json:
"psr-0": {
"Interfaces": "app/models/interfaces"
}
And I even added this in my start/global.php file:
ClassLoader::addDirectories(array(
app_path().'/models/interfaces',
In my recent laravel 5 project, I'm used to prepare my logics as Repository method.
So here's my current directory structure. For example we have 'Car'.
So first I just create directory call it libs under app directory and loaded it to composer.json
"autoload": {
"classmap": [
"database",
"app/libs" //this is the new changes (remove this comment)
]
}
after that I create a subfolder call it Car . Under the Car folder create two file 'CarEloquent.php' for eloquent implementation and CarInterface.php as interface.
CarInterface
namespace App\libs\Car;
interface CarInterface {
public function getAll();
public function create(array $data);
public function delete($id);
public function getByID($id);
public function update($id,array $data);
}
CarEloquent
namespace App\lib\Car;
use App\lib\Car\CarInterface;
use App\Car; //car model
class CarEloquent implements CarInterface {
protected $car;
function __construct(Car $a) {
$this->car = $a;
}
public function getAll(){
return $this->car->all();
}
}
Then create Car Service Provider to bind ioc controller.
For create Car service provider you can also use php artisan command by laravel.
php artisan make:provider CarServiceProvider
ServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class CarServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind('App\lib\Car\CarInterface', 'App\lib\Car\CarEloquent');
}
}
And final step would be add these service provider to config/app.php provider array.
'providers' => [
'App\Providers\CatServiceProvider',
]
And finally we are ready to use our repository method in our controller.
Example Controller
namespace App\Http\Controllers;
use App\lib\Car\CarInterface as Car;
class CarController extends Controller {
protected $carObject;
public function __construct(Car $c) {
$this->carObject = $c;
}
public function getIndex(){
$cars = $this->carObject->getAll();
return view('cars.index')->with('cars',$cars);
}
}
Main purpose to achieve here call repository method to controller, however you need use them as per your requirement.
Update
CarEloqent basically help us to improve database implementation, for example in future if you want to implement same functionality for other database like redis you just add another class CarRedis and change implementation file path from server provider.
Update 1: Good Resource
http://programmingarehard.com/2014/03/12/what-to-return-from-repositories.html
[book] From Apprentice to Artisan by Taylor Otwell
Very good explanation about repository method and software design principle commonly called separation of concerns. You should read this book.
If you still have any confusion to achieve these behaviors let me know and however I will keep eye on this question to update this answer, if I find some things to change or update or as per requirement.
Related
I want to define some methods which can be used in multiple place or multiple controllers. Basically these methods will be like libraries which will perform multiple queries.
My main aim is to avoid writing common logic multiple times by creating some libraries.
Please help me with it.
Thanks in advance :)
Depends what are you trying to do. Here are some options:
By default all your controllers extend App\Http\Controllers\Controller class. Just put all the shared logic between controllers there.
For complex queries to the database you can create a Repository and and inject in the controllers.
class UserRepository {
public function getActiveUsers() {
return Users::with('role')
->where('...')
->someQueryScopes()
->anotherQueryScope()
->yetAnotherScope();
}
}
class SomeController extends Controller {
public function index(UserRepository $repository) {
$users = $repository->getActiveUsers();
return view('users.index')->withUsers($users);
}
}
Yet another option is to create a Service classes for business logic and inject them in the constructor or relevant methods
class UserCreatorService {
public function create($email, $password){
$user = User::create(['email' => $email, 'password' => $password]);
$user->addRole('Subscriber');
Event::fire(new UserWasCreated($user));
return $user;
}
}
class RegisterController extends Controller {
public function store(Request $request, UserCreatorService $service) {
$user = $service->create($request->input('email'), $request->input('password'));
return view('home')->withUser($user);
}
}
it's simple, build your own library in your app folder then create new file MyLibrary.php
namespace App;
Class MyLibrary {
public static function sum($a, $b) {
return $a + $b;
}
}
then create alias in your config/app.php
'MyLibrary' => App\MyLibrary::class,
and finally you can call it in anywhere your controller
$result = MyLibrary::sum(4, 5); // your $result now have value of 9
You can make a folder named lib and inside that a functions.php file and in composer.json
...
"autoload": {
"files": [
"app/lib/functions.php"
],
...
and run composer dump-autoload
Use helper classes and create common functions in them.
Create a folder named helper in app folder and create a helper class in it. And then use that helper class's function in multiple controller or views as you want.
I want information about the system locale to be available in every view, so I could highlight whatever language is currently selected by a user. After some googling around, I've found the value-sharing issue addressed in the official documentation. However, after putting the code into boot() like this:
class AppServiceProvider extends ServiceProvider{
public function boot(){
view()->share('locale', \Lang::getLocale());
}
}
the $locale variable, when accessed in views, always holds the default system locale, not the currently selected one. Why?
I usually use View Composers so it's more clear and readable.
For example If I want to share a variable with the main navbar to all of my views I follow the below rules:
1. Create new service provider
You can create a service provider with artisan cli:
php artisan make:provider ViewComposerServiceProvider
In the ViewComposerServiceProvider file create composeNavigation method in which has the blade template main.nav-menu that represents the navmenu with shared variables.
The ViewComposerServiceProvider looks like:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ViewComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
$this->composeNavigation();
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
private function composeNavigation()
{
view()->composer('main.nav-menu', 'App\Http\ViewComposers\NavComposer');
}
}
2. Create Composer
As you saw in the file above we have App\Http\ViewComposers\NavComposer.php so let's create that file. Create the folder ViewComposers in the App\Http and then inside create NavComposer.php file.
The NavComposer.php file:
<?php
namespace App\Http\ViewComposers;
use App\Repositories\NavMenuRepository;
use Illuminate\View\View;
class NavComposer
{
protected $menu;
public function __construct(NavMenuRepository $menu)
{
$this->menu = $menu;
}
public function compose(View $view)
{
$thing= $this->menu->thing();
$somethingElse = $this->menu->somethingElseForMyDatabase();
$view->with(compact('thing', 'somethingElse'));
}
}
3. Create repository
As you saw above in the NavComposer.php file we have repository. Usually, I create a repository in the App directory, so create Repositories directory in the App and then, create inside NavMenuRepository.php file.
This file is the heart of that design pattern. In that file we have to take the value of our variables that we want to share with all of our views.
Take a look at the file bellow:
<?php
namespace App\Repositories;
use App\Thing;
use DB;
class NavMenuRepository
{
public function thing()
{
$getVarForShareWithAllViews = Thing::where('name','something')->firstOrFail();
return $getVarForShareWithAllViews;
}
public function somethingElseForMyDatabase()
{
$getSomethingToMyViews = DB::table('table')->select('name', 'something')->get();
return $getSomethingToMyViews;
}
}
For people with small project:
Firstly, The accepted answer is awesome!
For Laravel 5.2 users:
Just use the new blade directive #inject within your views like this
#inject('shared','App\Utilities\SharedWithView')
then you can use it:
{{ $shared->functionName() }}
And SharedWithView is a simple class like this one:
namespace App\Utilities;
use App\Repositories\SomeRepositoryLikeArticlesRepository;
class SharedWithView {
public function functionName() {
$properNameHere = new SomeRepositoryLikeArticlesRepository();
return $properNameHere->forEaxmpleGetMostViewedArticles( 10 );
}
}
I want information about the system locale to be available in every view, so I could highlight whatever language is currently selected by a user. After some googling around, I've found the value-sharing issue addressed in the official documentation. However, after putting the code into boot() like this:
class AppServiceProvider extends ServiceProvider{
public function boot(){
view()->share('locale', \Lang::getLocale());
}
}
the $locale variable, when accessed in views, always holds the default system locale, not the currently selected one. Why?
I usually use View Composers so it's more clear and readable.
For example If I want to share a variable with the main navbar to all of my views I follow the below rules:
1. Create new service provider
You can create a service provider with artisan cli:
php artisan make:provider ViewComposerServiceProvider
In the ViewComposerServiceProvider file create composeNavigation method in which has the blade template main.nav-menu that represents the navmenu with shared variables.
The ViewComposerServiceProvider looks like:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ViewComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
$this->composeNavigation();
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
private function composeNavigation()
{
view()->composer('main.nav-menu', 'App\Http\ViewComposers\NavComposer');
}
}
2. Create Composer
As you saw in the file above we have App\Http\ViewComposers\NavComposer.php so let's create that file. Create the folder ViewComposers in the App\Http and then inside create NavComposer.php file.
The NavComposer.php file:
<?php
namespace App\Http\ViewComposers;
use App\Repositories\NavMenuRepository;
use Illuminate\View\View;
class NavComposer
{
protected $menu;
public function __construct(NavMenuRepository $menu)
{
$this->menu = $menu;
}
public function compose(View $view)
{
$thing= $this->menu->thing();
$somethingElse = $this->menu->somethingElseForMyDatabase();
$view->with(compact('thing', 'somethingElse'));
}
}
3. Create repository
As you saw above in the NavComposer.php file we have repository. Usually, I create a repository in the App directory, so create Repositories directory in the App and then, create inside NavMenuRepository.php file.
This file is the heart of that design pattern. In that file we have to take the value of our variables that we want to share with all of our views.
Take a look at the file bellow:
<?php
namespace App\Repositories;
use App\Thing;
use DB;
class NavMenuRepository
{
public function thing()
{
$getVarForShareWithAllViews = Thing::where('name','something')->firstOrFail();
return $getVarForShareWithAllViews;
}
public function somethingElseForMyDatabase()
{
$getSomethingToMyViews = DB::table('table')->select('name', 'something')->get();
return $getSomethingToMyViews;
}
}
For people with small project:
Firstly, The accepted answer is awesome!
For Laravel 5.2 users:
Just use the new blade directive #inject within your views like this
#inject('shared','App\Utilities\SharedWithView')
then you can use it:
{{ $shared->functionName() }}
And SharedWithView is a simple class like this one:
namespace App\Utilities;
use App\Repositories\SomeRepositoryLikeArticlesRepository;
class SharedWithView {
public function functionName() {
$properNameHere = new SomeRepositoryLikeArticlesRepository();
return $properNameHere->forEaxmpleGetMostViewedArticles( 10 );
}
}
I'm building an application, now i'm created a helper
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
now i'm unable to do something like this in controller
namespace App\Http\Controllers;
class WelcomeController extends Controller
{
public function index()
{
return view('student/homepage');
}
public function StudentData($first_name = null)
{
/* ********** unable to perform this action *********/
$students = Student::return_student_names();
/* ********** unable to perform this action *********/
}
}
this is my helper service provider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
foreach(glob(app_path().'/Helpers/*.php') as $filename){
require_once($filename);
}
}
}
i event added it as an alias in config/app.php file
'Student' => App\Helpers\Students::class,
Try putting use App\Helpers\Student; at the top of your controller beneath the namespace delcaration:
namespace App\Http\Controllers;
use App\Helpers\Student;
class WelcomeController extends Controller
{
// ...
Look more into PHP namespaces and how they are used, I believe you may have a deficient understanding about them. Their only purpose is to make so you can name and use two classes with the same name (e.g. App\Helpers\Student vs maybe App\Models\Student). If you needed to use both of those classes inside of the same source file, you can alias one of them like this:
use App\Helpers\Student;
use App\Models\Student as StudentModel;
// Will create an instance of App\Helpers\Student
$student = new Student();
// Will create an instance of App\Models\Student
$student2 = new StudentModel();
You do not need to have a service provider for this, just the normal language features. What you would need a service provider for is if you wanted to defer the construction of your Student object to the IoC:
public function register()
{
$app->bind('App\Helpers\Student', function() {
return new \App\Helpers\Student;
});
}
// ...
$student = app()->make('App\Helpers\Student');
You should never have to include or require a class file in laravel because that is one of the functions that composer provides.
You do not need a service provider to make it works. Just lets the Students class as you did:
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
all its methods should be static
You added the Facade correctly:
'Student' => App\Helpers\Students::class,
Finally, looks like your problem is caused by forgetting a backslash at facade name. Uses \Students instead of Students:
public function StudentData($first_name = null)
{
$students = \Student::return_student_names();
}
When using a facade, it is not necessary makes nay include, the facades were made to avoid complex includes in everywhere.
I recently watched this video and wanted to change my Laravel controllers so that they had their dependencies managed with Laravel's IoC container. The video talks about creating an interface for a Model and then implementing that interface for the specific data source used.
My question is: when implementing the interface with a class that extends Eloquent and binding that class to the controller so that it is accessible from $this->model, should I also create interfaces and implementations for the Eloquent models which may be returned when calling methods such as $this->model->find($id)? Should there be different classes for the Model and the ModelRepository?
Put it another way: how do I do new Model when my model is in $this->model.
Generally, yes, people doing that pattern (the repository pattern) have an interface which have some methods defined that your app will use:
interface SomethingInterface {
public function find($id);
public function all();
public function paged($offset, $limit);
}
Then you create an implementation of this. If you're using Eloquent, then you can make an Eloquent implementation
use Illuminate\Database\Model;
class EloquentSomething {
protected $something;
public function __construct(Model $something)
{
$this->something = $something;
}
public function find($id)
{
return $this->something->find($id);
}
public function all() { ... }
public function paged($offset, $limit) { ... }
}
Then you make a service provider to put it all together, and add it into app/config/app.php.
use Something; // Eloquent Model
use Namespace\Path\To\EloquentSomething;
use Illuminate\Support\ServiceProvider;
class RepoServiceProvider extends ServiceProvider {
public function register()
{
$app = $this->app;
$app->bind('Namespace/Path/To/SomethingInterface', function()
{
return new EloquentSomething( new Something );
});
}
}
Finally, your controller can use that interface as a type hint:
use Namespace/Path/To/SomethingInterface;
class SomethingController extends BaseController {
protected $something;
public function __construct(SomethingInterface $something)
{
$this->something = $something;
}
public function home() { return $this->something->paged(0, 10); }
}
That should be it. Apologies on any errors, this isn't tested, but is something I do a lot.
Downsides:
More code :D
Upsides:
Able to switch out implementations (instead of EloquentSomething, can use ArraySomething, MongoSomething, whatever), without changing your controller code or any code that uses an implementation of your interface.
Testable - you can mock your Eloquent class and test the repository, or mock your constructor dependency and test your controller
Re-usable - you can App::make() to get the concrete EloquentSomething anywhere in your app and re-use the Something repository anywhere in your code
Repository is a good place to add additional logic, like a layer of cacheing, or even validation rules. Stock mucking about in your controllers.
Finally:, since I likely typed all that out and STILL DIDN'T ANSWER YOUR QUESTION (wtf?!), you can get a new instance of the model using $this->model. Here's an example for creating a new Something:
// Interface:
public function create(array $data);
// EloquentSomething:
public function create(array $data)
{
$something = this->something->newInstance();
// Continue on with creation logic
}
Key is this method, newInstance().
I've used $newModel = $this->model and it's worked for me.