Laravel Custom Facade New Instance - laravel

Working on my first laravel package and running into trouble with how my Facade works, currently my use looks something like this:
{!! Custom::showValue() !}} //returns "default"
{!! Custom::setValue('test')->showValue() !}} //returns "test"
{!! Custom::showValue() !}} //returns "test"
I would expect the last element to go be a new class instance, as I've used bind instead of singleton when setting up my service provider:
public function register()
{
$this->registerCustom();
}
public function registerCustom(){
$this->app->bind('custom',function() {
return new Custom();
});
}
Is there something else I need to do to make it so every facade call to "Custom" returns a new class instance?

As #maiorano84 mentioned you can not do this with Facades out of the box.
To answer your question, to make your Custom facade return a new instance you could add the following method to it:
/**
* Resolve a new instance for the facade
*
* #return mixed
*/
public static function refresh()
{
static::clearResolvedInstance(static::getFacadeAccessor());
return static::getFacadeRoot();
}
Then you could call:
Custom::refresh()->showValue();
(Obviously, you can call refresh something else if you want to)
One alternative to this would be to use the app() global function that comes with Laravel to resolve a new instance i.e.
app('custom')->showValue();
Hope this helps!

From laravel 9 you can use the $cached = false property inside your facade so it does not get cached:
class YourFacade extends Facade
{
protected static $cached = false;
protected static function getFacadeAccessor()
{
return ...;
}
}

Related

How to use preferredLocale in lumen?

With the release of Laravel 5.7 the Illuminate\Notifications\Notification class started offering a locale method to set the desired language. The application will change into this locale when the notification is being formatted and then revert back to the previous locale when formatting is complete. Here is an example of this feature:
$user->notify((new InvoicePaid($invoice))->locale('ar'));
I just need to use this feature in lumen (latest version) but when I implement that Like documentation said I got an error
Call to undefined method Laravel\Lumen\Application::getLocale()
and this because there is no getLocale or setLocale methods in lumen application.. so any ideas to solve this.
Difference between Lumen and Laravel is that in Laravel you call Application->setLocale().
This does three things, as outlined above:
Set config app.locale
Set Locale on the translator
Fire the locale.changed event
In Lumen though, you would call the translator directly with app('translator')->setLocale() or App::make('translator')->setLocale(),
so the difference here is that the config variable will not be set automatically and the locale.changed event will not be fired.
Laravel's Application class also updates the config and fires an event:
public function setLocale($locale)
{
$this['config']->set('app.locale', $locale);
$this['translator']->setLocale($locale);
$this['events']->fire('locale.changed', [$locale]);
}
and in Laravel, getLocale is just reading the config variable:
public function getLocale()
{
return $this['config']->get('app.locale');
}
For translations thought, it's the translator that matters. Laravel's trans helper looks like this:
function trans($id = null, $parameters = [], $domain = 'messages', $locale = null)
{
if (is_null($id)) {
return app('translator');
}
return app('translator')->trans($id, $parameters, $domain, $locale);
}
You need to make your application extends another class with the above 3 methods
You can Extend your Laravel\Lumen\Application in a new class and make $app variable take an instance from your new class in your bootstrap\app.php file
1- create the new class like this:
<?php namespace App\Core;
use Laravel\Lumen\Application as Core;
class Application extends Core
{
/**
* #param $locale
*/
public function setLocale($locale): void
{
$this['config']->set('app.locale', $locale);
$this['translator']->setLocale($locale);
$this['events']->fire('locale.changed', [ $locale ]);
}
public function getLocale()
{
return $this['config']->get('app.locale');
}
}
2- make an instance from your new class ex:
$app = new App\Core\Application(
realpath(dirname(__DIR__) . '/')
);

Laravel + Mockery InvalidCountException

I am trying to mock a class to prevent it from having to call 3rd party apis. But when setting up the mock, it doesn't seem to affect the controller action. I did try replacing the $this->postJson() by manually creating instances of the Request- and OEmbedController-classes. The create()-method is getting called, but I am receiving an error from Mockery that it isn't.
What am I doing wrong here?
Error:
Mockery\Exception\InvalidCountException : Method create() from Mockery_2_Embed_Embed should be called exactly 1 times but called 0 times.
Test:
class OEmbedTest extends TestCase
{
public function tearDown()
{
Mockery::close();
}
/**
* It can return an OEmbed object
* #test
*/
public function it_can_return_an_o_embed_object()
{
$url = 'https://www.youtube.com/watch?v=9hUIxyE2Ns8';
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
$response = $this->postJson(route('oembed', ['url' => $url]));
$response->assertSuccessful();
}
}
Controller:
public function __invoke(Request $request)
{
$info = Embed::create($request->url);
$providers = $info->getProviders();
$oembed = $providers['oembed'];
return response()
->json($oembed
->getBag()
->getAll());
}
It seems you are mocking the Embed class the wrong way. If you use the Laravel facade method shouldReceive() instead of creating a Mock of the class itself, the framework will place the mock in the service container for you:
Embed::shouldReceive('create')
->with($url)
->once();
instead of
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
Also be aware that if the parameters your tested code passes to the mock differs from what you learned the mock with with($url), the mock considers itself uncalled. But you'll receive another error for calling a not defined method anyway.
I was able to solve this by using this in my test:
protected function setUp()
{
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
}
Then resolving it like this
$embed = resolve(Embed::class);
$embed = $embed->create($url);

Extending the Laravel Eloquent\Collection Class

I understand that the default Eloquent\Collection class can be overridden in your model by using the method:
public function newCollection(array $models = array()) {
return new CustomCollection($models);
}
Which works great if I'm using typical queries such as:
Model::where('name', $name)->get();
This is great so I can add methods to the eloquent collection class, such as:
$records = Model::where('name', $name)->get();
$records->toTable();
But if I'm using pagination on the model, for example:
Model::where('name', $name)->paginate(25);
It returns an instance of the class Illuminate\Support\Collection instead of the Illuminate\Database\Eloquent\Collection.
Is there a way of overriding or extending the typical Illuminate\Support\Collection?
I'm trying to add a toTable() method to the returned Collection. I'd rather not have to replace the pagination service provider with my own.
Thanks!!
You will need to replace the pagination service provider, amongst a couple of other classes in the pagination library. By the sound of it you know how to do it this way, but were hoping for another answer, but as I have the code I'll drop it in here for you.
The reason you need to replace these classes/methods is because the files in Illuminate directly reference instances of classes within the Illuminate namespace.
In config/app.php
Replace
'Illuminate\Pagination\PaginationServiceProvider',
With
'ExtendedPaginationServiceProvider',
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationServiceProvider.php and place the following in it
<?php
use Illuminate\Support\ServiceProvider;
class ExtendedPaginationServiceProvider extends ServiceProvider
{
/**
* #inheritdoc
*/
public function register()
{
$this->app->bindShared('paginator', function($app)
{
$paginator = new ExtendedPaginationFactory($app['request'], $app['view'], $app['translator']);
$paginator->setViewName($app['config']['view.pagination']);
$app->refresh('request', $paginator, 'setRequest');
return $paginator;
});
}
}
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationFactory.php and place the following in it
<?php
use Illuminate\Pagination\Factory;
class ExtendedPaginationFactory extends Factory
{
/**
* #inheritdoc
*/
public function make(array $items, $total, $perPage = null)
{
$paginator = new ExtendedPaginationPaginator($this, $items, $total, $perPage);
return $paginator->setupPaginationContext();
}
}
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationPaginator.php and place the following in it
<?php
use Illuminate\Pagination\Paginator;
class ExtendedPaginationPaginator extends Paginator
{
/**
* Get a collection instance containing the items.
*
* #return ExtendedCollection
*/
public function getCollection()
{
return new ExtendedCollection($this->items);
}
}
You'll notice the above returns a new instance of ExtendedCollection. Obviously replace this with your CustomCollection class you refer to in your question.
For others to reference, an ExtendedCollection class may look similar to the below
Create a new file somewhere the autoloader is capable of finding it called ExtendedCollection.php and place the following in it
<?php
use Illuminate\Support\Collection;
class ExtendedCollection extends Collection
{
}
Also, after creating these files, don't forget to run the following in the terminal
composer dump-autoload

global variable for all controller and views

In Laravel I have a table settings and i have fetched complete data from the table in the BaseController, as following
public function __construct()
{
// Fetch the Site Settings object
$site_settings = Setting::all();
View::share('site_settings', $site_settings);
}
Now i want to access $site_settings. in all other controllers and views so that i don't need to write the same code again and again, so anybody please tell me the solution or any other way so i can fetch the data from the table once and use it in all controllers and view.
Okay, I'm going to completely ignore the ridiculous amount of over engineering and assumptions that the other answers are rife with, and go with the simple option.
If you're okay for there to be a single database call during each request, then the method is simple, alarmingly so:
class BaseController extends \Controller
{
protected $site_settings;
public function __construct()
{
// Fetch the Site Settings object
$this->site_settings = Setting::all();
View::share('site_settings', $this->site_settings);
}
}
Now providing that all of your controllers extend this BaseController, they can just do $this->site_settings.
If you wish to limit the amount of queries across multiple requests, you could use a caching solution as previously provided, but based on your question, the simple answer is a class property.
At first, a config file is appropriate for this kind of things but you may also use another approach, which is as given below (Laravel - 4):
// You can keep this in your filters.php file
App::before(function($request) {
App::singleton('site_settings', function(){
return Setting::all();
});
// If you use this line of code then it'll be available in any view
// as $site_settings but you may also use app('site_settings') as well
View::share('site_settings', app('site_settings'));
});
To get the same data in any controller you may use:
$site_settings = app('site_settings');
There are many ways, just use one or another, which one you prefer but I'm using the Container.
Use the Config class:
Config::set('site_settings', $site_settings);
Config::get('site_settings');
http://laravel.com/docs/4.2/configuration
Configuration values that are set at run-time are only set for the current request, and will not be carried over to subsequent requests.
In Laravel, 5+ you can create a file in the config folder and create variables in that and use that across the app.
For instance, I want to store some information based on the site.
I create a file called site_vars.php,
which looks like this
<?php
return [
'supportEmail' => 'email#gmail.com',
'adminEmail' => 'admin#sitename.com'
];
Now in the routes, controller, views you can access it using
Config::get('site_vars.supportEmail')
In the views if I this
{{ Config::get('site_vars.supportEmail') }}
It will give email#gmail.com
Hope this helps.
EDiT-
You can also define vars in .env file and use them here.
That is the best way in my opinion as it gives you the flexibility to use values that you want on your local machine.
So, you can do something this in the array
'supportEmail' => env('SUPPORT_EMAIL', 'defaultmail#gmail.com')
Important - After you do this, don't forget to do this on production env
php artisan config:cache
In case, there's still some problem, then you can do this (usually it would never happen but still if it ever happens)
php artisan cache:clear
php artisan config:cache
In your local env, always do this after this adding it
php artisan config:clear
It's always a good practice not to cache config vars in local. in case, it was cached, this would remove the cache and would load the new changes.
I see, that this is still needed for 5.4+ and I just had the same problem, but none of the answers were clean enough, so I tried to accomplish the availability with ServiceProviders. Here is what i did:
Created the Provider SettingsServiceProvider
php artisan make:provider SettingsServiceProvider
Created the Model i needed (GlobalSettings)
php artisan make:model GlobalSettings
Edited the generated register method in \App\Providers\SettingsServiceProvider. As you can see, I retrieve my settings using the eloquent model for it with Setting::all().
public function register()
{
$this->app->singleton('App\GlobalSettings', function ($app) {
return new GlobalSettings(Setting::all());
});
}
Defined some useful parameters and methods (including the constructor with the needed Collection parameter) in GlobalSettings
class GlobalSettings extends Model
{
protected $settings;
protected $keyValuePair;
public function __construct(Collection $settings)
{
$this->settings = $settings;
foreach ($settings as $setting){
$this->keyValuePair[$setting->key] = $setting->value;
}
}
public function has(string $key){ /* check key exists */ }
public function contains(string $key){ /* check value exists */ }
public function get(string $key){ /* get by key */ }
}
At last I registered the provider in config/app.php
'providers' => [
// [...]
App\Providers\SettingsServiceProvider::class
]
After clearing the config cache with php artisan config:cache you can use your singleton as follows.
$foo = app(App\GlobalSettings::class);
echo $foo->has("company") ? $foo->get("company") : "Stack Exchange Inc.";
You can read more about service containers and service providers in Laravel Docs > Service Container and Laravel Docs > Service Providers.
This is my first answer and I had not much time to write it down, so the formatting ist a bit spacey, but I hope you get everything.
I forgot to include the boot method of SettingsServiceProvider, to make the settings variable global available in views, so here you go:
public function boot(GlobalSettings $settinsInstance)
{
View::share('globalsettings', $settinsInstance);
}
Before the boot methods are called all providers have been registered, so we can just use our GlobalSettings instance as parameter, so it can be injected by Laravel.
In blade template:
{{ $globalsettings->get("company") }}
View::share('site_settings', $site_settings);
Add to
app->Providers->AppServiceProvider file boot method
it's global variable.
Most popular answers here with BaseController didn't worked for me on Laravel 5.4, but they have worked on 5.3. No idea why.
I have found a way which works on Laravel 5.4 and gives variables even for views which are skipping controllers. And, of course, you can get variables from the database.
add in your app/Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
// Using view composer to set following variables globally
view()->composer('*',function($view) {
$view->with('user', Auth::user());
$view->with('social', Social::all());
// if you need to access in controller and views:
Config::set('something', $something);
});
}
}
credit: http://laraveldaily.com/global-variables-in-base-controller/
In Laravel 5+, to set a variable just once and access it 'globally', I find it easiest to just add it as an attribute to the Request:
$request->attributes->add(['myVar' => $myVar]);
Then you can access it from any of your controllers using:
$myVar = $request->get('myVar');
and from any of your blades using:
{{ Request::get('myVar') }}
In Laravel 5.1 I needed a global variable populated with model data accessible in all views.
I followed a similar approach to ollieread's answer and was able to use my variable ($notifications) in any view.
My controller location: /app/Http/Controllers/Controller.php
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use App\Models\Main as MainModel;
use View;
abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function __construct() {
$oMainM = new MainModel;
$notifications = $oMainM->get_notifications();
View::share('notifications', $notifications);
}
}
My model location: /app/Models/Main.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use DB;
class Main extends Model
{
public function get_notifications() {...
I have found a better way which works on Laravel 5.5 and makes variables accessible by views. And you can retrieve data from the database, do your logic by importing your Model just as you would in your controller.
The "*" means you are referencing all views, if you research more you can choose views to affect.
add in your app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Contracts\View\View;
use Illuminate\Support\ServiceProvider;
use App\Setting;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
// Fetch the Site Settings object
view()->composer('*', function(View $view) {
$site_settings = Setting::all();
$view->with('site_settings', $site_settings);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
}
If you are worried about repeated database access, make sure that you have some kind of caching built into your method so that database calls are only made once per page request.
Something like (simplified example):
class Settings {
static protected $all;
static public function cachedAll() {
if (empty(self::$all)) {
self::$all = self::all();
}
return self::$all;
}
}
Then you would access Settings::cachedAll() instead of all() and this would only make one database call per page request. Subsequent calls will use the already-retrieved contents cached in the class variable.
The above example is super simple, and uses an in-memory cache so it only lasts for the single request. If you wanted to, you could use Laravel's caching (using Redis or Memcached) to persist your settings across multiple requests. You can read more about the very simple caching options here:
http://laravel.com/docs/cache
For example you could add a method to your Settings model that looks like:
static public function getSettings() {
$settings = Cache::remember('settings', 60, function() {
return Settings::all();
});
return $settings;
}
This would only make a database call every 60 minutes otherwise it would return the cached value whenever you call Settings::getSettings().
You can also use Laravel helper which I'm using.
Just create Helpers folder under App folder
then add the following code:
namespace App\Helpers;
Use SettingModel;
class SiteHelper
{
public static function settings()
{
if(null !== session('settings')){
$settings = session('settings');
}else{
$settings = SettingModel::all();
session(['settings' => $settings]);
}
return $settings;
}
}
then add it on you config > app.php under alliases
'aliases' => [
....
'Site' => App\Helpers\SiteHelper::class,
]
1. To Use in Controller
use Site;
class SettingsController extends Controller
{
public function index()
{
$settings = Site::settings();
return $settings;
}
}
2. To Use in View:
Site::settings()
A global variable for using in controllers; you can set in AppServiceProvider like this :
public function boot()
{
$company=DB::table('company')->where('id',1)->first();
config(['yourconfig.company' => $company]);
}
usage
config('yourconfig.company');
using middlwares
1- create middlware with any name
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\View;
class GlobalData
{
public function handle($request, Closure $next)
{
// edit this section and share what do you want
$site_settings = Setting::all();
View::share('site_settings', $site_settings);
return $next($request);
}
}
2- register your middleware in Kernal.php
protected $routeMiddleware = [
.
...
'globaldata' => GlobalData::class,
]
3-now group your routes with globaldata middleware
Route::group(['middleware' => ['globaldata']], function () {
// add routes that need to site_settings
}
In file - \vendor\autoload.php, define your gobals variable as follows, should be in the topmost line.
$global_variable = "Some value";//the global variable
Access that global variable anywhere as :-
$GLOBALS['global_variable'];
Enjoy :)
I know I am super late to the party, but this was the easiest way I found.
In app/Providers/AppServiceProvider.php, add your variables in the boot method. Here I am retrieving all countries from the DB:
public function boot()
{
// Global variables
view()->composer('*',function($view) {
$view->with('countries', Country::all());
});
}
There are two options:
Create a php class file inside app/libraries/YourClassFile.php
a. Any function you create in it would be easily accessible in all the views and controllers.
b. If it is a static function you can easily access it by the class name.
c. Make sure you inclued "app/libraries" in autoload classmap in composer file.
In app/config/app.php create a variable and you can reference the same using
Config::get('variable_name');
Hope this helps.
Edit 1:
Example for my 1st point:
// app/libraries/DefaultFunctions.php
class DefaultFunctions{
public static function getSomeValue(){
// Fetch the Site Settings object
$site_settings = Setting::all();
return $site_settings;
}
}
//composer.json
"autoload": {
"classmap": [
..
..
..
"app/libraries" // add the libraries to access globaly.
]
}
//YourController.php
$default_functions = new DefaultFunctions();
$default_functions->getSomeValue();

Call method from (custom) controller class in Magento

I'm working with the M2e extension for Magento. Now I want to call a method of the class Ess_M2ePro_Adminhtml_ListingController in the file app/code/community/Ess/M2ePro/controllers/Adminhtml/ListingController.php.
But I don't know, how. I can't create an object or model to get access to the class to use the methods. Maybe it's not a good idea to call this controller methods directly, but in my case (remove a associated magento product to an ebay listing) it's required to call this methods.
In general these actions are called from the magento backend. I've also tried to create an admin_html session, but at the moment I don't have any further ideas.
Here's an example, how it looks like. I'm working with regular PHP-code, nothing special:
class Ess_M2ePro_Adminhtml_ListingController extends Ess_M2ePro_Controller_Adminhtml_MainController
{
//#############################################
protected function _initAction()
{
/** removed **/
}
protected function _isAllowed()
{
return Mage::getSingleton('admin/session')->isAllowed('m2epro/listings/listing');
}
//#############################################
public function indexAction()
{
/** removed **/
}
//#############################################
public function searchAction()
{
/** removed **/
}
public function searchGridAction()
{
/** removed **/
}
public function lockListingNowAction()
{
$listingId = (int)$this->getRequest()->getParam('id');
$component = $this->getRequest()->getParam('component');
$lockItemParams = array(
'id' => $listingId,
'component' => $component
);
$lockItem = Mage::getModel('M2ePro/Listing_LockItem',$lockItemParams);
if (!$lockItem->isExist()) {
$lockItem->create();
}
exit();
}
}
And I'm looking for something like this:
$test = Mage::getModel('M2ePro/Ess_M2ePro_Adminhtml_ListingController')->lockListingNowAction();
You shouldn't call methods from an other controller. Specially in your case, when you have exit at the end of the method.
You can use the _forward method if you are in a controller:
$this->_forward($action = 'lockListingNowAction', $controller = 'adminhtml_listing', $module = 'M2ePro', $params = array('id'=>$id)) //controller name may be different
But the cleanest way is to have the code you need in a helper and call the code from that helper in both controllers.

Resources