I want to know how methods are declared in Laravel's facades. For example, I want to create a user-defined function to index my login page. Firstly, I need to check whether the user is already authenticated. To do that, I will use Laravel's Auth facade.
public function indexLogin() {
if (Auth::check()) {
return redirect('/mainpage');
}
}
But, when I wanted to learn more about this method, the only thing I came across were declarations made in the PHPDoc section.
/*
*
* #method static bool check()
*
*/
For this case, I know what the method does but also want to know how it works. I believe the declarations that were made in PHPDoc sections are not enough to run methods.
I checked Laravel's official documentation but found nothing.
You see at the end of the methods declaration, before the class name declaration there is a PHPDoc :
#see \Illuminate\Auth\AuthManager
#see \Illuminate\Contracts\Auth\Factory
#see \Illuminate\Contracts\Auth\StatefulGuard
#see \Illuminate\Contracts\Auth\Guard
you can check them to know how the method works.
In the documentation, you can see where the methods come from as pointed out by #xenooooo.
by digging a bit, you cas see that check() is using user()
/**
* Determine if the current user is authenticated.
*
* #return bool
*/
public function check()
{
return ! is_null($this->user());
}
Related
We have a Laravel 8 application.
We're using the standard Laravel Auth facade to retrieve the authenticated user.
Our User model has a few custom functions, the most important of which is a shorthand function, hasPermissionTo(). (The reason why is because we have a very custom RBAC setup.)
So in a lot of our controllers, we have something like this...
use Illuminate\Routing\Controller as BaseController;
class ExampleController extends BaseController
{
public function index()
{
if (\Auth::user()->hasPermissionTo('Management:View Users')) {
// do something.
}
// etc.
}
}
That's all well and good until we start running static analysis. We're using Larastan, which is giving me these errors:
------ -------------------------------------------------------------------------------------------
Line Http/Controllers/ExampleController.php
------ -------------------------------------------------------------------------------------------
48 Call to an undefined method Illuminate\Contracts\Auth\Authenticatable::hasPermissionTo().
This also makes sense because the Auth facade proxies Illuminate\Auth\AuthManager and Auth::user(), via __call() magic, normally resolves to Illuminate\Auth\SessionGuard::user() and that typehints this...
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
...
So finally, my question:
Where is the failure here? Do I need to a) configure my static analysis tool better, b) configure Laravel better to more accurately return a specific type, or c) do I need to add explicit if (Auth::user() instanceof User) { ... } clauses all throughout my code?
Is there a correct way to override one of the Laravel stock classes with a more specific one of my own to address more specific functionality? Is there way to type-hint the actual authenticated User into the function declaration so I can declare function index(User $authenticatedUser) and have Laravel autopopulate this in with a more specific type hint?
I understand that I could just add an exclusion for this particular issue in Larastan and move on with my life, but the error is designed to protect against a specific class of error--i.e. if I added Auth0 and replaced App\Model\User with Auth0\Login\User, then I would have an Authenticatable class that fails to run hasPermissionTo(), and I'd have to now fix a bunch of code.
Eventually, this is how we worked around the problem. We added a type-hint for Larastan, so it can infer that $user has this HasRolesContract trait which provides hasPermissionTo().
public function index()
{
/** #var \App\Traits\HasRolesContract */
$user = \Auth::user();
if ($user->hasPermissionTo('Management:View Users')) {
Hopefully this helps someone else!
(Thanks for the nudge, #djjavo)
I need to add $variable in my php return document
for example I have this function :
/**
* #return \Panel\Model\{$Service}
*/
public function getService($Service){
// I call service `foo` from Model folder
}
I see this post : What's the meaning of #var in php comments but it has no information about how to do it , and also study #var-phpdoc but that has nothing for me.
if you ask me why I should do that , because I want use phpStorm Ctrl+Click advantage on $this->getService('foo')->bar()
thanks in advance
As LazyOne said in the comments already PHP interfaces should solve your problem. You can 't use variables in PHPDoc formatted comments. Sure, if you use an IDE like PHPStorm with a plugin that enables the usage of variables in PHPDoc comments, the problem is solved for yourself. What, when other developers, which don 't use PHPStorm or the relevant plugin, want to work in the same project? In my view you should use php native functionality to solve your issue.
Here 's a short example how to use interfaces.
declare('strict_types=1');
namespace Application\Model;
interface ModelInterface
{
public function getFoo() : string;
public function setFoo() : ModelInterface;
}
The only thing you have to do now is using this interface with your models like in the following example.
declare('strict_types=1');
namespace Application\Model;
class FooModel implements ModelInterface
{
protected $foo = '';
public function getFoo() : string
{
return $this->foo;
}
public function setFoo(string $foo) : ModelInterface
{
$this->foo = $foo;
return $this;
}
}
As you can see the FooModel class implements the ModelInterface interface. So you have to use the methods declared in the interface in you model class. This means, that your getService Method could look like the following example.
/**
* Some getter function to get a model
* #return \Application\Model\ModelInterface
*/
public function getService($service) : ModelInterface
{
return $service->get(\Application\Model\Foo::class);
}
Your IDE knows now which methods the returned class can use. It allows you to use chaining and some more features. While typing your IDE should know now, that the returned class can use getFoo and setFoo methods. Further the setFoo methods enables comfortable chaining for calls like ..
// variable contains the string 'foo'
// your ide knows all methods
$fooString = $this->getService($serviceLocator)->setFoo('foo')->getFoo();
Are you using Symfony? Then you could use the Symfony plugin that solves this problem for you. For other frameworks, there should be similar solutions. If you use your own framework, you need to write such a plugin on your own, as PhpStorm can not resolve the given class otherwise.
I think what you are looking for is phpdoc.
https://docs.phpdoc.org/guides/docblocks.html
/**
* #param string $Service This is the description.
* #return \Panel\Model\{$Service}
*/
public function getService($Service){
// I call service `foo` from Model folder
}
I'm wondering if there's a simple way to override a singleton service set in the core of the Laravel framework?
e.g. I'm trying to rewrite the app:name command service '' with the following provider:
use Hexavel\Console\AppNameCommand;
use Illuminate\Console\Events\ArtisanStarting;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ServiceProvider;
class NameCommandProvider extends ServiceProvider
{
/**
* Register any other events for your application.
*
* #param \Illuminate\Contracts\Events\Dispatcher $events
* #return void
*/
public function boot(Dispatcher $events)
{
$events->listen(ArtisanStarting::class, function ($event) {
$event->artisan->resolve('command.app.name');
}, -1);
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->singleton('command.app.name', function ($app) {
return new AppNameCommand($app['composer'], $app['files']);
});
}
}
I'm 100% everything is working due to extensive checks put no matter what order I put my service provider (above or below ConsoleSupportServiceProvider) it still loads the original AppNameCommand over my custom one.
I've already got a work around BUT it would be nice to know about the behaviour of singleton services for the future if this is at all possible? (This is using Laravel 5.2 if that makes any difference.)
There's actually a cleaner way to do this. You basically want to extend a core binding, which can be achieved by using the extend method:
$this->app->extend('command.app.name', function ($command, $app) {
return new AppNameCommand($app['composer'], $app['files']);
});
Jason Lewis has a really nice article regarding Laravel's IoC on Tutsplus. Make sure to check it out ;)
I looked at this case and it seems it not the easy one. If you use singleton in your custom Provider it will be finally overridden by default provider (deferred one) so it seems it won't be the way.
After checking that simple approach doesn't work, what you need to do in such case is analysing what is happening when Laravel registers this command.
So in your case you search first for command.app.name - you see it's in Illuminate\Foundation\Providers\ArtisanServiceProvider and there is method registerAppNameCommand you would like to probably override.
So now you look for occurences of ArtisanServiceProvider to see where it's launched - you see it's in Illuminate\Foundation\Providers\ConsoleSupportServiceProvider in $providers property (which you would like probably to change).
So finally you should look for occurrences of ConsoleSupportServiceProvider and you see it's in config/app.php.
So what you need to do in this case:
Change in config/app.php - change Illuminate\Foundation\Providers\ConsoleSupportServiceProvider into your custom one ConsoleSupportServiceProvider
In your custom one you should extend from \Illuminate\Foundation\Providers\ConsoleSupportServiceProvider but change in $providers from Illuminate\Foundation\Providers\ArtisanServiceProvider into your custom ArtisanServiceProvider
finally create custom ArtisanServiceProvider which will extend from \Illuminate\Foundation\Providers\ArtisanServiceProvider where you override registerAppNameCommand using custom class in singleton
Using this way you will achieve your goal (I've verified it that custom class will be used running command php artisan app:name).
Alternatively you might want in your custom ArtisanServiceProvider remove 'AppName' => 'command.app.name', from $devCommands and use your custom service provider as you showed where you register your singleton but I haven't tried this approach.
I get
Call to undefined method ContestsCpController::getAfterFilters()
on a specific controller. All other controllers are working fine and I do not remember any change that would cause this breakage. In fact, I haven't touched the code in weeks. The last thing I did was some refactoring.
Route
Route::get("contestscp/home", "ContestsCpController#getHome");
Controller
<?php
class ContestsCpController extends BaseController
{
public function getHome() {
return Redirect::to("contestscp/give_award");
}
...
some other methods
...
}
?>
Debug output
/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php
* #param \Illuminate\Routing\Route $route
* #param \Illuminate\Http\Request $request
* #param string $method
* #return mixed
*/
protected function assignAfter($instance, $route, $request, $method)
{
foreach ($instance->getAfterFilters() as $filter) //fails here
{
// If the filter applies, we will add it to the route, since it has already been
Google and SO suggest that this is caused when controller does not extend BaseController but this is obviously not the case. So I assume that for some reason my class is not being extended. Or.. the class fails to initialize and $instance is null. But I have no idea why and how to debug this.
Any suggestions?
I knew this had to be something stupid.. because it always is.
The problem was my refactoring. I used to have all validators extended in a single file. When I separated the validators into different files I misnamed my ContestsCpValidator class as ContestsCPController (duh..). So I had a second class with the same name with no methods obviously.
So basically, if you happen to have this error and you are indeed extending the BaseController make sure you don't autoload another class with the same name.
I'm playing around with packages and I'm able to my code to work (in my controllers) when I do this:
App::make('Assets')->js('bla');
Now I want to set up a static facade so I can do this:
Assets::js('bla');
for this and I'm getting errors. I've been following this blog entry and haven't had any trouble up to this point. But now I'm stuck with a " Call to undefined method" error.
I'm not sure what code you'd need to see, so here's everything: https://github.com/JoeCianflone/msl/tree/jc-working
Specifically here is my workbench: https://github.com/JoeCianflone/msl/tree/jc-working/workbench/Joecianflone/Assets
And here is the controller where I was messing around with it: https://github.com/JoeCianflone/msl/blob/jc-working/app/controllers/HomeController.php
Any help greatly appreciated.
Looks like it was an issue with namespacing, I got it working by changing this:
<?php namespace Joecianflone\Assets\Facades;
use Illuminate\Support\Facades\Facade;
class Assets extends Facade {
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'Assets'; }
}
to this:
class Assets extends \Illuminate\Support\Facades\Facade {
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'Joecianflone\Assets\Assets'; }
}
What I'm not sure about is why the code from the tutorial worked but mine didn't. I must have skipped a step.
Just a sidenote, if you plan to share your code with the communuty (please do) i encourage you to use 5.3 syntax. Laravel requirements is 5.3 so dont use 5.4 in your package.