Looked up a few tutorials on facades and laravel 4... tried some... not liked the way they work.
For instance, they don't all provide a way of defining where to store the facade files and service providers... and i tried to step away from that and got my head bumped into a few walls until i decided to do this thread.
So: Let's say i have an app called Laracms (laravel cms).
I'd like to store everything i create - facades, service providers, etc in a folder under app named laracms.
So i'd have /app/laracms/facades, /app/laracms/serviceproviders and so on. I don't want to mix the facades with the database models, i want to keep things as separate as possible.
Let's take now, in my case, the Settings name for the facade (i want to implement a settings class to use in views and admin to set up misc. stuff).
Settings::get(), Settings::set() as methods.
Can anyone explain how to set facades up correctly? I don't know what i'm doing wrong and i need a fresh start.
Thanks,
Chris
Looking for a step by step with simple explanations of how and why.
First you need to go to app/config/app.php and in providers section add:
'Laracms\Providers\SettingsServiceProvider',
In the same file in aliases section you should add:
'Settings' => 'Laracms\Facades\Settings',
In your app/Laracms/Providers you should create file SettingsServiceProvider.php
<?php
namespace Laracms\Providers;
use Illuminate\Support\ServiceProvider;
class SettingsServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('settings', function()
{
return new \Laracms\Settings();
});
}
}
In your app/Laracms/Facades/ you should create file Settings.php:
<?php
namespace Laracms\Facades;
use Illuminate\Support\Facades\Facade;
class Settings extends Facade {
protected static function getFacadeAccessor() { return 'settings'; }
}
Now in your app/Laracms directory you should create file Settings.php:
<?php
namespace Laracms;
class Settings {
public function get() {echo "get"; }
public function set() {echo "set"; }
}
As you wanted to have your files in custom folder Laracms you need to add this folder to your composer.json (If you used standard app/models folder you wouldn't need to add anything to this file). So now open composer.json file and in section autoload -> classmap you should add app/Laracms so this section of composer.json could look like this:
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php",
"app/Laracms"
]
},
Now you need to run in your console inside your project foler:
composer dump-autoload
to create class map
If everything is fine, you should now be able to use in your applications Settings::get() and Settings:set()
You need to notice that I used folders with uppercases because namespaces by convention starts with upper letters.
There are three components to making a Facade:
The wanna be Facade Class, that class that needs to become a facade.
The Facade required Class, which tells Laravel which registered class it pertains to
A Service Provider, which registers the Facade class in the App container
1. the wanna be Facade Class:
<?php namespace Moubarmij\Services\ModelsServices;
class AuthenticationService extends MoubarmijService implements AuthenticationServiceInterface{
/**
* #param $email
* #param $password
*
* #return mixed
*/
public function login($email, $password)
{
return Sentry::authenticate([
'email' => $email,
'password' => $password,
]);
}
/**
* #return mixed
*/
public function logout()
{
return Sentry::logout();
}
}
2. the required class for the facade to work:
<?php namespace Moubarmij\Facades;
use Illuminate\Support\Facades\Facade;
/**
* Class AuthenticationServiceFacade
* #package Moubarmij\Services\ModelsServices
*/
class AuthenticationServiceFacade extends Facade{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'authentication_service'; }
}
note: authentication_service can be anything you want (its the name of the component registered in the IOC)
3. the service provider
<?php namespace Moubarmij\Providers;
use Illuminate\Support\ServiceProvider;
/**
* A service provider for the Authentication Service
*
* Class AuthenticationServiceSP
* #package Moubarmij\Providers
*/
class AuthenticationServiceSP extends ServiceProvider {
/**
* bind interfaces
*
* #return void
*/
public function register()
{
// Register 'authentication_service' instance container to our AuthenticationService object
$this->app['authentication_service'] = $this->app->share(function($app)
{
return $app->make('Moubarmij\Services\ModelsServices\AuthenticationService');
});
// Shortcut to auto add the Alias in app/config/app.php
$this->app->booting(function()
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('AuthenticationService', 'Moubarmij\Facades\AuthenticationServiceFacade');
});
}
}
Related
I would like to create a component in yii2 that can be accessed throughout the web application but only create one instance and be able to retrieve that instance wherever needed.
namespace app\components;
use yii;
use yii\base\Object;
class ContentManagerComponent extends Object
{
public function init(){
parent::init();
}
public function toBeUsed (){
return 'some variable';
}
}
Then I want to be able to call the component in other parts of the web application, like in the controllers.
namespace app\Controllers;
use yii;
use app\controllers\
class SomeController extends Controller {
public function actionDoSomething(){
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed()
return $this->render( 'someView',[
'variable' => $someVariable,
]
}
}
I have also put the component in the web.php file.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
What I'm ending up with is phpstorm telling me that the class doesn't exist. I would also like to have intelisense like the other components do in the application.
intelisense:
noIntele:
update:#
I was able to get intelisense working by adding the this line as suggested by the answer below. /** #var ContentComponent $contentManager */
However I got tired of always typing that out above each time I wanted to use the Content Component. So I created a function in the base class of the components I was needing Content Component that return the Continent Component using the Yii::app->content method. Above the function that would return the Content Component I added the comment that it would return ContentComponent and the class of the ContentComponent. Now in order for me to use the component with intelisense working. All I would have to do is $this->getContentComponent. Php storm would be able to identify that the content component was of the class returned. Bellow is an example.
class BaseClass extends object
{
/**
* #return ContentComponent
*/
function getContentComponent () {
$contentComponent = Yii::app->content;
return $contentComponent
}
}
class SomeClass extends BaseClass
public function someFunction () {
$contentComponent = $this->getContentComponent;
}
PHPStorm don't recognize your custom component because they are created dynamically on framework load and attached to Yii::$app on runtime, That's why PHPStorm don't recognize your custom components. So until someone will develop an intelligent plugin for IDEs like PHPStorm, you will have to make some tweaks to achieve your goals.
You have 2 options:
Create a new Yii.php file (in root dir) for reference with all the
necessary docs and this will tell PHPStorm in the entire project
about your components (I putted here a full example, if you want to create components which available only for console/web/both) look at * #property ContentManagerComponent $content (More read - credit to samdark Alexander Makarov, one of Yii core contributors):
<?php
use app\components\ContentManagerComponent;
use yii\BaseYii;
/**
* Class Yii
* Yii bootstrap file.
* Used for enhanced IDE code autocompletion.
*/
class Yii extends BaseYii
{
/**
* #var BaseApplication|WebApplication|ConsoleApplication the application instance
*/
public static $app;
}
/**
* Class BaseApplication
* Used for properties that are identical for both WebApplication and ConsoleApplication
*
* #property ContentManagerComponent $content
*/
abstract class BaseApplication extends yii\base\Application
{
}
/**
* Class WebApplication
* Include only Web application related components here
*
*/
class WebApplication extends yii\web\Application
{
}
/**
* Class ConsoleApplication
* Include only Console application related components here
*/
class ConsoleApplication extends yii\console\Application
{
}
Create a PHP doc everywhere you want to use your component which
will tell PHPStorm that your variable is instance of the component:
public function actionDoSomething()
{
/** #var ContentManagerComponent $contentComponent */
$contentComponent = Yii::$app->content;
$someVariable = $contentComponent->toBeUsed();
return $this->render('someView', [
'variable' => $someVariable,
]);
}
As you can see option 1 is a solution provided by one of the core contributors of the Yii framework, so I assumes that this suppose to be the right choice for now (until there will be native support by JetBrains or any plugin)
Once you have declared your content component in you config files
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
],
],
then you can refer tor the component using
Yii::$app->content
eg
Yii::$app->content->yourMethod();
eventually add use Yii; or refer using \Yii::$app->content
I use the following method for intellisense.
1.Set your components inside config.
$config = [
'components' => [
'content' => [
'class' => 'app\components\ContentManagerComponent',
],
'content2' => [
'class' => 'app\components\ContentManagerComponent2',
],
],
],
2.Have an AppComponents trait, documenting all instances that your $app has. I like to have it inside components/ directory.
<?php
namespace app\components;
/**
* Trait AppComponents
* #package app\components
*
* #property ContentManagerComponent1 $content
* #property ContentManagerComponent2 $content2
*/
trait AppComponents {}
3.Return the Yii::$app your own way. Trick the editor into believing that AppComponents may be returned.
<?php
namespace app\controllers;
use frontend\components\AppComponents;
use yii\rest\Controller;
class SiteController extends Controller {
/**
* #return \yii\web\Application|AppComponents
*/
public static function app() {
return \Yii::$app;
}
}
Now you can use SiteController::app()->content with intellisense. You can have a nicer Root class, and replace \Yii::$app with Root::app(). All Controllers may inherit from the Root class. You can also use self::app() when coding inside the extended Controllers.
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 trying to get the hang of dependancy injection and the IoC container in Laravel.
Currently I have a class full of static methods that I use in my views. E.g.
class Templatizer {
/**
* Generates a colored FontAwsome check or cross from a true/false argument
* #param boolean $bool
* #return string The HTML markup of the icon
*/
public static function boolicon($bool)
{
return $bool ? '<span class="fa fa-check text-success"></span>' : '<span class="fa fa-times text-danger"></span>';
}
}
I have composer autoload the class and in my view I can just go {{ Templatizer::boolicon($trueOrFalseValue) }}.
Clearly this is bad practice and I'd like to move away from using static methods. I presume the correct way is to inject an instance of Templatizer and use the methods something like {{ $templatizer->boolicon($v) }}`.
How would I structure this? Presumably I'd need to inject an instance of Templatizer into my controller via the constructor? e.g.
class PagesController extends BaseController {
protected $templatizer;
public function __construct(Templatizer $templatizer)
{
$this->templatizer = $templatizer;
}
}
And then for, say, the method for the index page I pass the instance to the view? e.g.
# inside PagesController
public function index()
{
return View::make('pages.index', ['templatizer' => $this->templatizer]);
}
If this is correct, where is an appropriate place to put my Templatizer class? How would I bind it to the IoC container?
First of all I don't see anything wrong with calling these methods statically. It looks like this is just a HTML helper class of your own and you should be fine.
If you decide to go with Dependency Injection the "correct" way to register your class would be using Service Providers.
Assigning it to the views the way you wrote would work but you can also get it once you need it this way:
$templatizer = App::make('Yournamespace\Templatizer');
And finally probably the best solution in your case would be building your own Facade.
Yes you should inject it via the controller constructor, the files can live anywhere you like as long as they are autoloaded in.
I like to create a folder in the root directory called src so that my composer.json file looks like:
"autoload": {
"classmap": [
....
],
"psr-4": {
"Foo\\": "src/"
}
}
Then you can have src/Templatizer.php which would look like:
<?php namespace Foo;
class Templatizer {
}
Now you just need a service provider to bind your instance of Templatizer (this basically makes Laravel aware of your class and allows you to inject it into your controllers) in src/FooServiceProvider.php
<?php namespace Foo;
use Illuminate\Support\ServiceProvider;
class FooServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred
*
* #var boolean
*/
protected $defer = false;
/**
* Register the service provider
*/
public function register() {
$this->app->bind('Foo\Templatizer', function($app) {
return new Templatizer();
});
}
}
Don't forget to add Foo\FooServiceProvider to the providers array in app config and you should be all set to do...
public function __construct(Foo\Templatizer $templatizer) {
You are also create instance on BaseController.
class BaseController extends Controller {
/**
* Setup the layout used by the controller.
*
* #return void
*/
var $templatizer;
protected function setupLayout()
{
if ( ! is_null($this->layout))
{
$this->layout = View::make($this->layout);
$templatizer = new \Templatizer();
View::share('templatizer', $templatizer);
}
}
}
use this $templatizer instance in a all view.
something like {{ $templatizer->boolicon($v) }}`.
In using the laravel framework, how can I call a function defined in base_controller, in a view. For exacmple:
class Base_Controller extends Controller {
public static function format_something()
{
return something;
}
}
How can i call format_something() in a view file?
Usually the error I get looks something like this:
Method [link_to_action] is not defined on the View class.
Probably a silly question, but thanks in advance!
Edit
Okay! First the correct place to do something like this is in the libraries folder.
Second, problem is that your class cannot have underscores.
So in application/libraries I made file AppHelper.php with class
class AppHelper {
public static function format_something()
{
return something;
}
}
And can call it like:
$formated = AppHelper::format_something;
Thanks for the help and the good forum find Boofus McGoofus.
For me is working:
Create directory "helpers" or whatever and file:
// app/helpers/AppHelper.php
class AppHelper {
public static function format_something()
{
return something;
}
}
Add path to composer.json
// composer.json
"autoload": {
"classmap": [
"app/helpers" // <-------- add this line
]
},
Run: (reload the autoload)
composer dump-autoload
Now you can call:
$formated = AppHelper::format_something();
This answer was written for Laravel 3. For Laravel 4 and after, Lajdák Marek's answer using Composer's autoloader is better.
Functions like format_something() don't belong in the controller. The controller should just be about collecting data from various sources and passing it to the view. It's job is mostly just routing.
I've created a folder called "helpers" in the application folder for all my little helpery functions. To make sure all my controllers, views, and models have access to them, I've included the following in my start.php file:
foreach(glob(path('app').'helpers/*.php') as $filename) {
include $filename;
}
I suspect that there's a better way to do that, but so far it has worked for me.
You can inspire yourself from Laravel framework itself.
I will take your example of a formatter and refer to url helper in Laravel Framework.
Start by creating your own helpers.php file:
<?php
if (! function_exists('format_that')) {
/**
* Generate something
*
* #param string $text
* #return string
*/
function format_that($text)
{
return app('formatter')->format_that($text);
}
}
And add it to your composer.json file:
"autoload": {
"files": [
"app/helpers/helpers.php"
]
}
Run this command to recreate the autoload php file:
$ composer dumpautoload
Create your service provider app/Providers/FormatterServiceProvider.php:
<?php
namespace Illuminate\Routing;
use Illuminate\Support\ServiceProvider;
use App\Helpers\FormatGenerator;
class FormatterServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app['formatter'] = $this->app->share(function ($app) {
return new FormatGenerator($app['request']);
});
}
}
Register your service provider. Laravel framework invokes register method but you only need to add it to your app config file config/app.php:
'providers' => [
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
// other providers...
App\Providers\FormatterServiceProvider::class,
]
Finally, create your actual generator class app/Helpers/FormatGenerator.php
<?php
namespace App\Helpers;
use Illuminate\Http\Request;
class FormatGenerator
{
protected $request;
/**
* Create a new URL Generator instance.
*
* #param \Illuminate\Routing\RouteCollection $routes
* #param \Illuminate\Http\Request $request
* #return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
public function format_that($text){
if ($request->path() == "home"){
return mb_strtoupper($text);
}
else{
return $text;
}
}
}
You can optionally create a Facade app/Facade/Formatter.php, to be able to do Formatter::format_that($text):
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* #see \App\Helpers\FormatGenerator
*/
class Formatter extends Facade
{
protected static function getFacadeAccessor() { return 'formatter'; }
}
You could ask yourself:
Why the facade? You can reuse the component somewhere else by simply calling Formatter::format_that($text) instead of app('formatter')->format_that($text). Sugar syntax really.
Why the Service provider? Dependence injections. If you need to use Request or want to build a complex object, the Service provider will take care of that for you and make it available in your $app object.