laravel create service provider - php

i make a serviceprovider and add provider in app.php but how can i use it ?
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Helpers\api\gg\gg;
class ApiServiceProvider extends ServiceProvider
{
protected $defer = true;
public function boot()
{
}
public function register()
{
$this->app->bind(gg::class, function ()
{
return new gg;
});
}
public function provides()
{
return [gg::class];
}
}
gg class is in App\Helpers\api\gg folder and i want use this class everywhere like that
gg::isReady();
app.php
'providers' => [
...
App\Providers\ApiServiceProvider::class,
...
]
homecontroller#index
public function index()
{
//how can use this provider in there ?
return view('pages.home');
}

When you did $this->app->bind(), you've bound an instance of a class to the IoC. When you bind to the IoC you make that available throughout the entirety of the application. HOWEVER:
Your namespaces break PSR-1 compliance. This is because you are not using StudlyCaps.
BAD: use App\Helpers\api\gg\gg
GOOD: use App\Helpers\Api\GG\GG.
Rename your folders/files accordingly. With that sorted, your bind function should actually change to a singleton. This is because you want a persistent state, not a reusable model.
$this->app->singleton(GG::class, function(){
return new GG;
});
You also should not check ->isReady() in every function, that's an example of an anti-pattern. Instead, this should be in a middleware:
php artisan make:middleware VerifyGGReady
Add this to your Kernel:
protected $routeMiddleware = [
//other definitions
'gg_ready' => App\Http\Middleware\VerifyGGReady::class
];
Update the handle() function in your middleware:
public function handle($request, Closure $next) {
if ($this->app->GG->isReady()) {
return $next($request);
}
return redirect('/'); //gg is not ready
});
And then either initialize it in your route groups:
Route::group(['middleware' => ['gg_ready']], function(){
//requires GG to be ready
});
Or directly on a route:
Route::get('acme', 'AcmeController#factory')->middleware('gg_ready');
Or use it in your controller:
$this->middleware('gg_ready');

Related

Simple access authorization with Lumen

Trying to implement simple access authorization with Lumen. It works when doing the update (PUT) action.
But I would also like to handle accessing for example all articles.
I also tried the viewAny or view policy method but no success.
Router
$router->group(['prefix' => 'api/v1'], function () use ($router) {
$router->get('articles', ['uses' => 'ArticleController#showAllArticles']);
$router->get('articles/{id}', ['uses' => 'ArticleController#showOneArticle']);
$router->post('articles', ['uses' => 'ArticleController#create']);
$router->delete('articles/{id}', ['uses' => 'ArticleController#delete']);
$router->put('articles/{id}', ['uses' => 'ArticleController#update']);
});
AuthServiceProvider
class AuthServiceProvider extends ServiceProvider
{
public function boot()
{
Gate::policy('App\Article', 'App\Policies\ArticlePolicy');
$this->app['auth']->viaRequest('api', function ($request) {
return app('auth')->setRequest($request)->user();
});
}
}
Policies
namespace App\Policies;
use App\User;
use App\Article;
class ArticlePolicy
{
public function showAllArticles(User $user, Article $post)
{
// not working
return true;
}
public function update(User $user, Article $post)
{
// this works
return true;
}
}
Controller
namespace App\Http\Controllers;
use App\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
public function showAllArticles()
{
$this->authorize('showAllArticles');
return response()->json(Article::all());
}
public function showOneArticle($id)
{
return response()->json(Article::find($id));
}
public function update($id, Request $request)
{
$article = Article::findOrFail($id);
$this->authorize('update', $article);
$article->update($request->all());
return response()->json($article, 200);
}
}
As per the Laravel documentation on Authorization:
"When defining policy methods that will not receive a model instance, such as a create method, it will not receive a model instance. Instead, you should define the method as only expecting the authenticated user:"
public function create(User $user)
So:
public function showAllArticles(User $user)
"As previously discussed, some actions like create may not require a model instance. In these situations, you should pass a class name to the authorize method. The class name will be used to determine which policy to use when authorizing the action:"
$this->authorize('create', Post::class);
So:
$this->authorize('showAllArticles', Article::class);
Laravel 7.x Docs - Authorization - Writing Policies - Methods without Models
Laravel 7.x Docs - Authorization - Authorizing Actions Using Policies - via Controller Helper authorize
No explanation needed.

Authorization gate not called during service provider

I have an admin gate defined in my AuthServiceProvider that is used to add global query scopes to some models. Suppose I had models A, that is observed by an Observer (registered in AppServiceProvider), and B, that makes use of the admin gate to add global query scopes.
// app/Providers/AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
public function boot()
{
Gate::define('admin', [static::class, 'admin']);
}
public static function admin(User $user): bool
{
return $user->group->name === 'Admin';
}
}
// app/B.php
class B extends Eloquent
{
public static function boot()
{
parent::boot();
if (!Gate::allows('admin')) {
static::addGlobalScope('public', function ($query) {
$query->where('public', true);
});
}
}
}
Up to this point everything worked fine. Then I added a model C that has an Observer and uses the admin gate. As C::observe() fires C::boot() and the AppServiceProvider is registered before the AuthServiceProvider the gate was not defined and I extracted the Observer registration to a new ObserverServiceProvider that is registered after AuthServiceProvider.
// app/C.php
class C extends Eloquent
{
public static function boot()
{
parent::boot();
if (!Gate::allows('admin')) {
static::addGlobalScope('public', function ($query) {
$query->where('public', true);
});
}
}
}
// app/Providers/ObserverServiceProvider.php
class ObserverServiceProvider extends ServiceProvider
{
public function boot()
{
A::observe(AObserver::class);
C::observe(CObserver::class);
}
}
// config/app.php
'providers' => [
//...
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
//...
App\Providers\ObserverServiceProvider::class,
]
My problem:
The observers for A and C are still working, as well as the admin gate in B's boot() method, but Gate::allows('admin') in C always returns false without even calling the gate function.
Adding a var_dump(Gate::has('admin')) in C::boot() outputs true and using #can('admin') later in the View during the same request works fine as well, so the gate is definitely defined and working in principle.
The authorization gate cannot be called as the session data (and therefore the authenticated user) is made available by the StartSession middleware, which runs after the service providers.
The problem can be solved by putting the Gate::allows() check inside the anonymous function, so it is only executed when building a query:
// app/C.php
class C extends Eloquent
{
public static function boot()
{
parent::boot();
static::addGlobalScope('public', function ($query) {
if (!Gate::allows('admin')) {
$query->where('public', true);
}
});
}
}

Laravel 5.2 Package : Auth methods fail in constructor of controller

I have added a controller for my package and I need to call Auth methods inside the constructor of this controller but I get the following error :
ReflectionException in Container.php line 734:
Class hash does not exist
Here is my code :
use Auth;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Session;
class CartController extends Controller
{
private $customer;
public function __construct()
{
$this->middleware('auth', ['except' => ['add']]);
$multiauth = config('cart.multiauth');
if ($multiauth) {
$guard = config('auth.defaults.guard');
$this->customer = Auth::guard($guard)->user();
} else {
$this->customer = Auth::user();
}
}
public function add()
{
// Code
}
}
When I add the code of constructor inside the other functions it works properly but it fails when it is called from constructor of the controller.
I have searched alot for this and found no working solution.
I've solved the problem by adding a middleware :
namespace myNamespace\myPackage;
use Closure;
use Illuminate\Support\Facades\Auth;
class CustomerMiddleware
{
public function handle($request, Closure $next)
{
$multiauth = config('cart.multiauth');
if ($multiauth) {
$guard = config('auth.defaults.guard');
$customer = Auth::guard($guard)->user();
} else {
$customer = Auth::user();
}
$request->attributes->add(['customer' => $customer]);
return $next($request);
}
}
Then I used this middleware for the 'cart/add' route :
Route::group(['middleware' => ['web']], function () {
Route::group(['middleware' => 'customer'], function() {
Route::post('cart/add',
'myNamespace\myPackage\CartController#add');
});
});
So by checking the $request->get('customer') parameter inside the 'add' method of 'CartController', I have access to information of current user :
class CartController extends Controller
{
public function __construct() { }
public function add()
{
$customer = $request->get('customer');
// Code
}
}
I hope this helps someone else :)
You can't use middleware in controller __construct , create a functions and use it

Mockery "shouldReceive()->once()" throwing "called exactly 1 times" error

I am about $this->close() to giving up on Mockery in my unit tests. Here's what's going on, I am working with Laravel 5.1 and I'm trying to test my repository pattern abstraction using Mockery in PHPUnit. I've followed the tutorials, poured over the StackOverflow questions so it's not a duplicate. When you see anything about modules here, it's PingPong Sky Modules package.
Basically, when I try to mock the repository interface and set shouldReceive('create')->with([])->once() , Mockery throws:
Mockery\Exception\InvalidCountException: Method create(array()) from Mockery_0_Modules_Documents_Repositories_DocumentRepositoryInterface should be called exactly 1 times but called 0 times.
DocumentsTest.php
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
class FileUploadTest extends TestCase {
use WithoutMiddleware;
public function mock($class){
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testFileUpload(){
// Mock the Repository
$mock = $this->mock('\Modules\Documents\Repositories\DocumentRepositoryInterface');
$mock->shouldReceive('create')->with([])->once();
$this->call('POST', '/documents', [], [], []);
$this->assertResponseStatus(201);
}
}
DocumentRepositoryInterface.php
<?php namespace Modules\Documents\Repositories;
interface DocumentRepositoryInterface {
public function create(array $data);
}
DatabaseDocumentRepository.php
<?php namespace Modules\Documents\Repositories;
use Modules\Documents\Repositories\DocumentRepositoryInterface;
use \Illuminate\Database\Eloquent\Model;
class DatabaseDocumentRepository implements DocumentRepositoryInterface {
protected $documents;
public function __construct(Model $documents) {
$this->documents = $documents;
}
public function create(array $data) {
// Eloquent code.
return "response";
}
}
Document.php
<?php namespace Modules\Documents\Entities;
use Illuminate\Database\Eloquent\Model;
class Document extends Model {
protected $fillable = [];
}
routes.php
$this->app->bind(
'Modules\Documents\Repositories\DocumentRepositoryInterface', function(){
return new Modules\Documents\Repositories\DatabaseDocumentRepository(new Modules\Documents\Entities\Document());
});
Route::group(['prefix' => 'documents', 'namespace' => 'Modules\Documents\Http\Controllers'], function(){
Route::post('/', ['as' => '/', 'uses'=> 'DocumentsController#create']);
});
DocumentsController.php
<?php namespace Modules\Documents\Http\Controllers;
use Modules\Documents\Repositories\DocumentRepositoryInterface;
use Pingpong\Modules\Routing\Controller;
use Module;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
class DocumentsController extends Controller {
private $documents;
public function __construct(DocumentRepositoryInterface $doc){
$this->documents = $doc;
}
public function create(Request $request){
$this->documents->create([]);
return response("", Response::HTTP_CREATED);
}
}
I want to say it has something to do with the mocked object not getting injected into the DocumentsController because the create() function is getting called. I put a print_r in the create function and it displayed in my console. This is strange and it could also be related to PingPong Sky Modules. What am I doing wrong or not doing?

Laravel 5 Resolving dependencies in ServiceProvider

I have a class which acts like a storage (add/get item). I try to bind it as a singleton in one service provider, and resolve it in another's boot method.
The code is changed for simplicity.
app/Providers/BindingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider as ServiceProvider;
class MyBindingFacade extends Facade {
public static function getFacadeAccessor() {
return 'my.binding';
}
}
class MyBinding {
protected $items = [];
public function add($name, $item) {
$this->items[$name] = $item;
}
public function get($name) {
return $this->items[$name];
}
public function getAll() {
return $this->items;
}
}
class BindingProvider extends ServiceProvider {
public function register() {
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
}
public function provides() {
return [
'my.binding',
];
}
}
app/Providers/ResolvingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider as ServiceProvider;
use App\Providers\MyBinding;
class ResolvingProvider extends ServiceProvider {
public function boot(MyBinding $binding) {
$binding->add('foo', 'bar');
// $manual = $this->app->make('my.binding');
// $manual->add('foo', 'bar');
}
public function register() {}
}
app/Http/Controllers/WelcomeController.php
<?php
namespace App\Http\Controllers;
use App\Providers\MyBindingFacade;
class WelcomeController extends Controller {
public function index()
{
dd(MyBindingFacade::getAll()); // debug items
}
}
When I try to debug MyBinding state in my WelcomeController I'm getting empty item array. However, if I uncomment $manual part from my ResolvingProvider it returns an array containing 'foo' => 'bar'. Does it mean IoC resolution is broken in ServiceProvider::boot() method or am I misusing Laravel functionality?
Laravel version: 5.0.28
UPDATE: Added code sample from WelcomeController.
With this:
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
You're saying: my.binding is a singleton and resolves to an instance of App\Providers\MyBinding.
That doesn't mean that App\Providers\MyBinding is registered as singleton too. What you should do instead is this:
$this->app->singleton('App\Providers\MyBinding');
$this->app->bind('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
Because the Facade binding uses $app->make() you should get the same instance you registered with $this->app->singleton() right above.
In the first example you are not using the Facade, you should be using:
use App\Providers\MyBindingFacade as MyBinding;
Which will in fact call make it using 'my.binding'.

Categories