Im new to Laravel and namespaces, but a colleague told me i have to use the namespaces and place all my models in a folder in the app directory, named after the project.
As far as i know this means that in every single controller that uses one or more models, have have to set "use" for every model my controller needs. Example:
<?php
use Foo\Entities\Entity;
use Foo\Entities\Inheritance;
use Foo\Entities\Type;
class EntitiesController extends BaseController {
public function index()
{
$inheritances = Inheritance::all();
$entities = Entity::all();
return View::make('settings/entities')
->with('entities', $entities)
->with('inheritances', $inheritances);
}
}
If we asume that the associated models above will be used everywhere, would it be completely crazy to put the models in the /app/model/ folder and if a controller need a model which overwrite the standard system, then use namespaces?
First things first: namespaces are NOT a Laravel thing, but a PHP feature created to better organize our code.
So, if you want your code organized, you should use namespaces for everything and, yes, you'll have to add 'use' clauses in the top of most of your PHP files. But in Laravel you also can be free to not use namespaces at all, you just have to add your autoload classes directories to your composer.json file:
"autoload": {
"classmap": [
"models"
],
},
Execute
composer dumpautoload
So Composer read all files in your models folder, to create a class map of them, and then you can just drop all uses clauses:
class EntitiesController extends BaseController {
public function index()
{
$inheritances = Inheritance::all();
$entities = Entity::all();
return View::make('settings/entities')
->with('entities', $entities)
->with('inheritances', $inheritances);
}
}
To not use namespaces in your PHP application, these days, may be considered a code smell. The only 'part' of Laravel where people doesn't usually use namespaces is Controllers, but this is also changing in Laravel 5, where controllers will be namespaced by default, but still, you will have the option to not use them, because this is a Composer/PHP thing, not Laravel.
Taylor Otwell has 3 big things always in mind when creating features and evolving Laravel: Best practices, fast coding and beautiful code.
EDIT
Answering your comment, if all your controllers needs to have access to some service or even model, why not add it to your BaseController?
But you may have to take a read at the repository pattern, because your controllers should not really be aware of your models. Developers are now creating a new layer (repository) between controllers and models, and perform the operations in those layers. You can, also, make use of Laravel Dependency Injection to help you with those use clauses you don't like.
It would be something like this:
Create a repository interface:
interface EntityRepositoryInterface {
}
Create the repository:
use Foo\Entities\Entity;
class EntityRepository {
public function find($id)
{
return Entity::find($id);
}
public function all()
{
return Entity::all();
}
}
Create your controllers using your repository:
class EntitiesController extends BaseController {
public function __construct(EntityRepositoryInterface $entityRepository)
{
$this->entityRepository = $entityRepository;
}
public function index()
{
$entities = $this->entityRepository->all();
return View::make('settings/entities')
->with('entities', $entities);
}
}
And you have to tell Laravel Dependency Injection what to instantiate when it needs a EntityRepositoryInterface:
App::bind('EntityRepositoryInterface', 'Foo\Entities\EntityRepository');
There's nothing wrong with putting your models anywhere you like. In fact, I put all my models that extend directly from Eloquent in app/models. You are free to follow this, or not follow it, in your own project.
However, this does come with a caveat. Very few of my classes actually directly interact with these models (Repositories are pretty much it). Those I do put in separate namespaces which are then injected into my controllers. Consequently, I must either use each repository that a controller needs at the top of each file or specify the fully qualified class name every time I reference it.
Related
First off, the issue/question arose with Laravel 5.6 and PHP 7.4.10. I know that 'magic strings' for controller calls have been deprecated as of recent Laravel versions, but I'm interested in the underlying infrastructure in this question.
PREREQUISITES
Suppose I have two folders: project and core.
project contains a freshly installed laravel project;
core contains some package-like structure of namespaces and classes (not actually a package in the laravel sense), which is being autoloaded by the project.
For a minimal working example, you can see the last commit of the repository I published about this. I'm going to post the main files' contents here as well, but you can actually reproduce and play around with the sample projects yourself.
Basically the concept is pretty standard, much like a package: I want to have some reusable logic inside of my core with some default implementations. In the project I may or may not overwrite some of the functions, but for the sake of the question's simplicity, my core only defines a Fruit module (just a folder with a namespace) containig three files:
fruit_routes.php
<?php
Route::get('/pear', 'FruitController#pear');
Route::get('/apple', 'FruitController#apple');
FruitController (This is where the question is focused)
<?php
namespace CORE\Fruit;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class FruitController extends Controller
{
public function pear()
{
//if we call the action '\CORE\Fruit\FruitController#apple' instead, it works :)
return redirect()->action('FruitController#apple', ['message' => "No pears available! :( "]);
}
public function apple(Request $request)
{
return $request->message . "There you have an apple!";
}
}
FruitServiceProvider
<?php
namespace CORE\Fruit;
use Illuminate\Support\ServiceProvider;
class FruitServiceProvider extends ServiceProvider
{
public function register()
{
/*
* Map routes from our Fruit module to the controller's namespace
*/
$this->app->router->namespace('CORE\Fruit')->group(__DIR__."/fruit_routes.php");
}
}
And then in the project I simply include the following two lines:
composer.json
{
//..
"autoload":
//...
"psr-4": {
"App\\": "app/",
"CORE\\" : "../core"
}
},
}
config/app.php in $providers array
/*
* Package Service Providers...
*/
CORE\Fruit\FruitServiceProvider::class,
Again, for the full sample structure and contents, refer to the MWE repo.
THE QUESTION
Although the namespace for the controller classes has been explicitly declared from the FruitServiceProvider's register() method for the fruit_routes.php file, Laravel still fails to recognize that same namespace when using rediret()->action() from within those same controllers.
In the given FruitController you can see:
return redirect()->action('FruitController#apple');
and this will fail with Action App\Http\Controllers\FruitController#apple not defined.
As you can see, Laravel still searches for the controller inside the current project's default controller namespace (in this case: App\Http\Controllers)
Fortunately, passing the fully qualified name of the controller solves the issue:
return redirect()->action([self::class, 'apple']);
//Or alternativelly, but really clumsy:
//return redirect()->action('\Core\Fruit\FruitController#apple'); //notice the slash at the beginning
I have the suspicion that I must somehow also inform the redirect() helper (and actually, the underlying Redirect facade) that I want it to reference the controllers inside of my CORE\Fruit namespace whenever called from within that namespace... Or what would be the right approach? Is that desired behavior and am I missing something? And again, as mentioned in the beginning, I know that 'magic strings' are not the best practice and the most beloved feature of Laravel, but I really want to understand how things work in this scenario.
I also found this issue on github with a realted topic, but no answer there as well. As for people who are about to comment 'just avoid using magic strings', I'm stuck with a project I inherited and rewriting all of the core's magic string controller references is really not an option I would love to embrace.
Any insight is appreaciated! Many many thanks in advance!
I'm trying to improve my code OOP wise.
I have the tendancy to put some of my bussiness logic inside my Controller.
Some people told me this is not desirable.
I know that when you are working with Posts for example. You can simply put your bussiness logic in your Post.php Model.
I now have a feature where i upload an .xlsx file and it has to be checked and validated if the format of the .xlsx is correct.
It is currently inside ScheduleController#storeDeliveryXlsx.
But i don't have a specified model for this to store the validation inside?
Is this where Repositories design pattern is needed? Or is it fine to create a model that doesn't communicate with the database like: DeliveryFile.php where i put the logic that i later can use throughout my app like this: DeliveryFile::validate($file); or DeliveryFile::upload(file);.
I think i'm likely to re-use the same validation on the .xlsx for multiple instances. So i'd like to do something like Model::validate($file);. But i don't know where to put the code.
In this case, I would personally recommend using a Service. A service class is essentially for common functionality that you may repeat across multiple controllers. The Service Layer is a design pattern that will help you to abstract your shared logic to a common service i.e. our new Service Class.
So in your instance, you could have a Service class such as ExcelValidator and then use something like the following to structure your approach.
<?php
namespace App/Services;
class ExcelValidatorService
{
public function validate($file)
{
// our validate logic
}
}
And then in your controllers or models, you can call the ExcelValidator service to implement your repeated validate logic.
The phrase I now have a feature would keep on repeating itself in the course of the lifetime of the code you are writing. Having separate controller, service, model classes, OOP design, modular code would definitely help in that regard. Now it might be easier, but it will keep on becoming difficult with time
Recently I faced a similar problem, in my case I need to use, as model, a Restful API.
In composer.json I added under autoload
"psr-4": {
"App\\": "app/",
"MYCOMP\\": "app/MYCOMP/"
},
Under app folders I added MYCOMP folder with two subfolders Providers and Restmodels.
In config/app.php in providers array I added only:
MYCOMP\Providers\RestModelServiceProvider::class,
In app/MYCOMP/Providers/RestModelServiceProvider.php I register all providers and facades for my rest models:
public function register()
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$models = glob( realpath(__DIR__."/../") ."/RestModels/*/*RestModel.php");
foreach($models as $model){
$bnmodel = basename($model);
if($bnmodel !== "BaseRestModel.php" && $bnmodel !== "BaseFacadeRestModel.php"){
list($n, $niente) = explode("RestModel.php", $bnmodel);
list($bnnoext, $niente) = explode(".php", $bnmodel);
$res = $n.'Repository';
$fold = basename(dirname($model));
$nc = "\\MYCOMP\\RestModels\\".$fold."\\".$bnnoext;
$this->app->bind($res, function($app) use ($nc) {
return new $nc;
});
$loader->alias($res, $nc."Facade");
}
}
}
This code is specific for my project and for how I structured folders.
For all rest models I have two files:
app/MYCOMP/RestModels/{ModelName}/{ModelName}RestModel.php
app/MYCOMP/RestModels/{ModelName}/{ModelName}RestModelFacade.php
The only exceptions are: BaseRestModel and BaseFacadeRestModel these classes are extended by all RestModel and RestModelFacade. So I'd implement something like validate under BaseRestModel.
BaseFacadeRestModel just contains:
<?php
namespace MYCOMP\RestModels\Base;
use Illuminate\Support\Facades\Facade;
class BaseFacadeRestModel extends Facade{
private $facadeName;
protected static function getFacadeName(){
list($senzasuffisso, $niente) = explode("RestModelFacade", static::class);
$nc = last(explode("\\", $senzasuffisso));
return $nc;
}
protected static function getFacadeAccessor() {
return static::getFacadeName().'Repository';
}
}
So all the other Facades work just exteding BaseFacadeRestModel.
I am very new to "Advanced Laravel" so to speak, however I do know most of the basics and I am trying to understand what namespacing, interfaces and repositories is all about, since I came across it not so long ago.
However, I am getting the following error, and I have no idea what I am doing wrong:
Class app\models\Interfaces\CategoriesInterface does not exist
Below is my code:
Routes.php
App::bind('App\Models\Interfaces\BaseInterface', 'App\Models\Repositories\BaseRepository');
CategoriesController.php
<?php
use app\models\Interfaces\CategoriesInterface;
class CategoriesController extends BaseController
{
protected $categories;
public function __construct(CategoriesInterface $categories)
{
$this->categories = $categories;
}
BaseInterface.php
<?php
interface BaseInterface
{
public function all();
}
CategoriesInterface.php
<?php namespace App\Models\Interfaces;
interface CategoriesInterface extends BaseInterface { }
CategoriesRepository.php
<?php namespace app\models\Repositories;
use App\Models\Interfaces\CategoriesInterface;
use Categories;
class CategoriesRepository implements CategoriesInterface
{
public function all()
{
$categories = $this->categories->all();
return $categories;
}
}
EloquentCategoriesRepository.php
<?php namespace app\models\Repositories;
use App\Models\Interfaces\CategoriesInterface;
class EloquentCategoriesRepository implements CategoriesInterface {
public function all()
{
return Categories::all();
}
Try name spacing the classes/interfaces properly. EloquentCategoriesRepository.php and CategoriesRepository are having app instead of App in the namespace. And CategoriesController too needs to use App\.. not app\...
I see you are trying to implement the repository pattern, which at first it might seem a bit 'advanced' but it's actually pretty simple.
So the basic idea is to abstract the data layer of your application with the database to make your transitions from one DBS to another(ex. Mysql to Mongo).
In other words your are trying to make the business logic of your application independent to the data layer (Where you query your collections/instances), so when you reach a point that you might want to change your database you can just implement another repository. The Interfaces are there to provide a contract between your application and the data layer.
Laravel implementation of the repository pattern it's pretty straight forward.
Create your interface
Create your interface's repository (actual implementation)
Bind the repository using a service provider class (Or in your case App::bind)
Instantiate the dependency in to your controller using the repository
Don't forget to auto load your namespaces using psr-04.
In your case I think the problem is you are not autoloading the namespace.
Also CategoriesRepository.php & EloquentCategoriesRepository.php are both Eloquent repositories and will return Eloquent collections. To return an array of stdClass (standar PDO) you will have to use the \DB facade.
If my answer does not cover you please take a look here
I'm trying to figure out how to add a method to a class in a Laravel package, so that all controllers and models that call that class can access the new method. How do I replace this class in the IoC?
This is the package in question, Angel CMS. The package is my creation, so I can modify it if we need to add aliases or anything to accomplish this.
Let's say I want to add a method to this class:
vendor/angel/core/src/models/PageModule.php
Okay, so I copy the class file to here:
app/models/PageModule.php
And then I modify the copied file, adding a namespace and the desired custom_function method:
<?php namespace MyModels;
use Eloquent;
class PageModule extends Eloquent {
protected $table = 'pages_modules';
public static function custom_function()
{
return 'It works!';
}
}
As you can see, I am using the MyModels namespace here.
Then, I run a composer dump-autoload.
Next, I open up my app/routes.php and register the binding and set up a test route:
App::bind('PageModule', function($app) {
return new \MyModels\PageModule;
});
Route::get('test-binding', function() {
return PageModule::custom_function();
});
But, when visiting the test route, I always receive the same error that the method is undefined.
What am I doing wrong here? Thank you in advance for any help.
To Clarify:
I am attempting to replace the class application-wide so that all other classes (controllers/models/etc.) that call PageModule will have access to the custom_function method. Thanks.
To be honest, I'm pretty new to all this IoC, dependency inversion/injection concept too. But I think I've gone through the same struggle before. What I would do, as much as my knowledge allows, is...
Add a constructor to src/controllers/admin/AdminPageController.php:
protected $pageModule;
public function __construct(PageModule $pageModule)
{
$this->pageModule = $pageModule;
}
Then where you did $module = new PageModule in the same file. You replace it with:
$module = $this->pageModule;
The two modifications above makes use of Laravel's IoC to allow injecting a different PageModule object into your controller, instead of strictly creating PageModule in your code.
Now at this point Laravel should know that when it constructs the AdminPageController, it should create a PageModule and inject into the controller for you.
Since your controller now expects a PageModule class, you can no longer do class PageModule extends Eloquent in your app anymore, because even though the name is the same, PHP does not think that it is! You'll need to extend it:
So let's rename your app/models/PageModule.php to app/models/CustomPageModule.php, and in the file change the class to:
class CustomPageModule extends \PageModule {
Up to this point, you also have a CustomPageModule class that is a child of your package's PageModule. All you need to do now is to let Laravel knows that if any controllers ask for PageModule, it should serve the controller with your MyModels\CustomPageModule instead.
So at the top of your app's routes.php file:
App::bind('PageModule', 'MyModels\CustomPageModule');
Your AdminPageController should now be using your CustomPageModule and can use whatever public methods that are in there!
I'm expecting to be editing this answer heavily since this will be quite a long discussion. My first try at answering above isn't the best code you can write, but I hope it takes the least amount of edit to the original code, and then we can work up from there.
Or fast track by reading up articles like http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories
You probably have a alias for the PageModule facade, you should override this alias using your class \MyModels\PageModule in your app/config/app.php file.
Be careful, it seems like you are overwriting the PageModule class instead of extending it. You should probably extend the parent class instead of Eloquent.
I have a view that shows a line of 10 numbers in a table (it's a dashboard of stats).
They are information on sales today, tomorrow, the day after etc, split by some different states.
Currently I have some code like:
class Transaction extends Eloquent {
// other methods that aren't relevant
public function scopeConfirmed($query)
{
return $query->where('status', '=', 'Confirmed');
}
public function scopeBooked($query)
{
return $query->where('status', '<>', 'Cancelled');
}
public function scopeDaysAhead($query, $days)
{
$start = \Carbon\Carbon::now()->addDays($days)->toDateString();
$end = \Carbon\Carbon::now()->addDays($days+1)->toDateString();
return $query->where('date', '>=', $start)->where('date', '<', $end);
}
// few other similar scopes
}
Then in my view I have:
(Simplified)
<td>
{{Transaction::->daysAhead(0)
->booked()
->count()}}
</td>
<td>
{{Transaction::->daysAhead(0)
->confirmed()
->count()}}
</td>
<td>
{{Transaction::->daysAhead(1)
->confirmed()
->count()}}
</td>
<td>
{{Transaction::->daysAhead(2)
->confirmed()
->count()}}
</td>
So, I'm dealing with Eloquent calls in my view. In reality there are scopes by sales person and by location, so there are 10-20 values being shown.
I can move this back into the Controller by having a very fat load of with statements, or of course put it into an Array in the controller, but that doesn't seem to help massively.
What is the best way to deal with this?
Keep your views as logic less as possible, don't clutter your controllers with logic either. This would, to most people mean stuffing all the logic into models. Again this isn't an approach that helps with writing re-usable and more importantly testable code.
Laravel's IoC container and dependency injection is incredibly powerful and should be utilised to structure your applications in a testable way.
I can understand why you want to pass the query object around as well, this is something I used to do in my models. It seems to make sense however it quickly becomes apparent that creating a rigid query builder using a powerful query builder will create some quantity of wet code.
My suggestion would be to keep your model as slim as possible, use them to create relationships, set eloquent properties, validation rule arrays and nothing more. Abstract all logic into a repository based on an interface. Why? Well, the interface can be bound through the IoC to the class it should resolve to meaning that it can be dependency injected and swapped out easily (Mockery for example) whilst maintaining structural integrity in any replacements you may want to build (a Mongo, CouchDB etc. implementation for example).
namespace Repositories;
interface TransactionInterface {
public function findAll();
public function findById($id);
public function findByDaysAhead($start = 0, $end = 1)
}
And for the repository
namespace Repositories;
use Transaction;
class TransactionEloquent implements TransactionInterface {
public function findAll()
{
return Transaction::all();
}
public function findById($id)
{
$transaction = Transaction::find($id);
if ( ! $transaction )
{
throw new Exception("Transaction not found");
}
return $transaction;
}
public function findByDaysAhead($start = 0 , $end = 1)
{
// Create one query to return all the data you need
}
}
You can then bind this repository either in a new custom ServiceProvider or in your routes.php.
App::bind('Repositories\TransactionInterface', 'Repositories\TransactionEloquent');
Now you can dependency inject into your controller the interface that will resolve to your eloquent implementation. If you write a different repository implementation you can simply rebind it to the interface meaning it'll be used wherever the interface is injected (Mockery classes for example)
class ApplicationController extends BaseController {
public function __construct(Repositories\TransactionInterface $interface)
{
$this->repo = $interface;
}
public function getIndex()
{
return View::make('index', array('transactions' => $this->repo->findAll());
}
}
In your view you would only require a simple loop over your data and outputting, no logic required.
Obviously you can put as much logic as you like in the repository and as you can see your controllers, models and views fulfil only the responsibility they are intended for (OOPS Single Responsibility Principle)
This is a very short answer to what is a very complex question. I hope this guides you someway to writing testable and reusable modular code in Laravel.
Some 'light' reading
Laravel 4's IoC container
Laravel 4's Service Providers
NetTuts article on building L4 and Backbone apps Ignore the backbone part, this is a great walkthrough on L4's IoC bindings.
Where to place files etc.
Following PSR-0 specifications I would end up with a structure like this
- app
- {name of app}
- Repositories
* TransactionInterface.php
* TransactionEloquent.php
The namespaces for these two files would now be namespace {name of app}\Repositories
In composer you can add this to the autoload object :
"psr-0": {
"{name of app}" : "app/"
}
This will add all PSR-0 compatible namespaces to the autoloader, when you make changes you can use
composer dump-autoload
to rebuild the autoloader and include your new files (not always required but is better and faster than composer update constantly).
How many repositories?
I usually end up with 1+ repositories per model. For example I have a backbone collection of models and individual models in the front end.
Using Backbone.sync(param, collection) would always utilise a repo that handles input as an array of models. Where as Backbone.sync(param, model) would utilise a repo that handled a single model being sent. I would also have two Laravel resource controllers to handle this as well.
Service Providers
I place these in my application folders route, my app is called "MFL" in this instance
- MFL
- Repositories
- MFLServiceProvider.php
I add this to my service providers array in config\app.php
namespace MFL;
use Illuminate\Support\ServiceProvider;
class MFLServiceProvider extends ServiceProvider {
public function register()
{
// Register bindings here, don't use other service providers here
// you can't be sure they are loaded as of yet
$this->app->bind('MFL\Repositories\TransactionInterface', 'MFL\Repositories\TransactionEloquent');
}
public function boot()
{
// Do anything else here with assurance all service providers are
// fully loaded and the application is ready
}
}
Using this method, you don't pollute routes.php with IoC bindings and you can logically partition all your code into services. This is my preferred method for the latter reason.
There is a 3rd option in view composers. At the most basic level these are included in routes.php but I like to break them out by view path so composers/transactions.php would handle all the views in views/transactions like 'transactions.dashboard'. Given that as a view then the composer would be...
View::composer('transactions.dashboard', function ($view)
{
$view->day0_count = Transaction::->daysAhead(0)
->booked()
->count();
}
$day0_count is now available in the view.
<td>{{ $day0_count }}</td>