I want to rewrite onceBasic() of Illuminate\Auth\SessionGuard by extending Facades\Auth, but encountering the problem of not being able to access the helper protected function of SessionGuard class.
Here is how I extends Auth facades:
namespace App\MyFacades;
use \Illuminate\Support\Facades\Auth;
class Auth_QuyLe extends Auth {
/**
* Create your custom methods here...
*/
public static function onceBasic_QuyLe($field = 'name', $extraConditions = [])
{
$credentials = self::basicCredentials(self::getRequest(), $field);
if (! self::once(array_merge($credentials, $extraConditions))) {
return self::failedBasicResponse();
}
}
}
When I call onceBasic_QuyLe() from my middleware, it shows
"Method Illuminate\Auth\SessionGuard::basicCredentials does not exist."
I've already updated my class to config/app.php and run composer dump-autoload && php artisan config:cache
I've finally found a solution:
Firstly, b/c SessionGuard is a “macroable” class, and if you need to add an additional method to SessionGuard , you need to go to AppServiceProvider and declare macros into the service provider’s boot method
public function boot()
{
SessionGuard::macro('myOnceBasic', function ($field = 'name', $extraConditions = []) {
$credentials = $this->basicCredentials($this->getRequest(), $field);
if (!$this->once(array_merge($credentials, $extraConditions))) {
return$this->failedBasicResponse();
}
});
}
and in the underlying class that your newly created Facades represents , you can use Auth facade to call the macros:
class MySessionGuard{
public function myOnceBasic($field = 'name', $extraConditions = [])
{
Auth::myOnceBasic($field = 'name', $extraConditions = []);
}
}
Related
I'm wondering what the best way to add things like weekends to the available schedule constraints:
Illuminate\Console\Scheduling\Event.php
public function weekdays()
{
return $this->spliceIntoPosition(5, '1-5');
}
and its logical opposite:
public function weekends()
{
return $this->days(array( '0','6'));
}
Where would I include these things so that they're not overwritten with a framework update?
First of all, if all you are missing is the weekends() method, you can achieve that by calling days(6,7) on your event.
If you need to add some more logic to the scheduler, please go on reading.
I had a look at the code and while Laravel doesn't offer a way to extend the Scheduler, and specifically its scheduled Events, with additional methoods, it's still possible to apply additional constraints from your Kernel::schedule().
Depending on your needs, there are 2 ways to do it.
If you want to set some custom CRON expression for an event, you can simply use its cron() method:
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
//scheduled code
})->cron('0 1 2 * * *')->daily();
}
If you need to apply some CRON constraints using existing methods, but need to modify it later the way weekdays() does using spliceIntoPosition, you can access it by calling getExpression(), modify it, and then set using cron().
protected function schedule(Schedule $schedule)
{
$event = $schedule->call(function () {
//scheduled code
});
$scheduledAt = $event->getExpression(); //get cron expression
...; //modify the $scheduledAt expression
$event->cron($scheduledAt); // set the new schedule for that even
}
If you want to reuse the logic for multiple events, you can add helper functions in your Kernel.php, e.g.:
protected function specialSchedule(\Illuminate\Console\Scheduling\Event $event) {
$scheduledAt = $event->getExpression();
...; // modify $scheduledAt expression
$event->cron($scheduledAt);
return $event;
}
Then you can reuse that logic when defining the schedule:
protected function schedule(Schedule $schedule)
{
$this->specialSchedule($schedule->call(function () {
//scheduled code
}));
}
UPDATE:
There is one more way to do that - a bit more complex, as it requires you to provide your own Schedule and Event classes, but also more flexible.
First, implement your own Event class and add there the new methods:
class CustomEvent extends \Illuminate\Console\Scheduling\CallbackEvent {
public function weekends() {
return $this->days(6,7);
}
}
Then your own Schedule class, so that it creates CustomEvent objects:
class CustomSchedule extends \Illuminate\Console\Scheduling\Schedule
{
public function call($callback, array $parameters = [])
{
$this->events[] = $event = new CustomEvent($callback, $parameters);
return $event;
}
public function exec($command, array $parameters = [])
{
if (count($parameters)) {
$command .= ' '.$this->compileParameters($parameters);
}
$this->events[] = $event = new Event($command);
return $event;
}
}
Lastly, in your Kernel.php you need too make sure your new schedule class is used for scheduling:
protected function defineConsoleSchedule()
{
$this->app->instance(
'Illuminate\Console\Scheduling\Schedule', $schedule = new Schedule
);
$this->schedule($schedule);
}
Folowing #jedrzej.kurylo's answer, i did this on laravel 5.8:
php artisan make:model CustomCallbackEvent
php artisan make:model CustomEvent
php artisan make:model CustomSchedule
In CustomCallbackEvent:
use Illuminate\Console\Scheduling\CallbackEvent;
use Illuminate\Console\Scheduling\EventMutex;
class CustomCallbackEvent extends CallbackEvent
{
public function __construct(EventMutex $mutex, $callback, array $parameters = [])
{
parent::__construct($mutex, $callback, $parameters);
}
}
In CustomSchedule:
use Illuminate\Console\Scheduling\Schedule;
class CustomSchedule extends Schedule
{
public function call($callback, array $parameters = [])
{
$this->events[] = $event = new CustomCallbackEvent(
$this->eventMutex,
$callback,
$parameters
);
return $event;
}
public function exec($command, array $parameters = [])
{
if (count($parameters)) {
$command .= ' '.$this->compileParameters($parameters);
}
$this->events[] = $event = new CustomEvent($this->eventMutex, $command, $this->timezone);
return $event;
}
}
In CustomEvent:
use Illuminate\Console\Scheduling\Event;
class CustomEvent extends Event
{
public function myFunction()
{
//your logic here
}
}
In Kernel.php:
protected function defineConsoleSchedule()
{
$this->app->instance(
'Illuminate\Console\Scheduling\Schedule', $schedule = new CustomSchedule
);
$this->schedule($schedule);
}
The Illuminate\Console\Scheduling\Event class uses the Macroable trait. It means that you can dynamically add methods to the class without inheriting it.
First of all you have to register it in the boot method:
use Illuminate\Console\Scheduling\Event;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Event::macro(
'weekends',
function () {
/** #var Event $this */
return $this->days([0, 6]);
}
);
}
}
Then you can use it as any other method:
$schedule->command('do-work')->weekends();
For more details about macros see https://asklagbox.com/blog/laravel-macros
I am quite new to ZF2 and I am preparing a demo application with simple login and CRUD system. Now for login I have prepared a plugin which consists of some functions that will authenticate users, return the logged in user data, return the logged in status etc. But the problem that I am facing is I can't initialize any variable into the constructor of my controller which will store any return value from the plugin. It's always showing service not found exception.
Please find my plugin code below:
AuthenticationPlugin.php
<?php
namespace Album\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Session\Container as SessionContainer;
use Zend\View\Model\ViewModel;
use Album\Entity\User;
class AuthenticationPlugin extends AbstractPlugin{
protected $entityManager;
protected $usersession;
public function __construct(){
$this->usersession = new SessionContainer('UserSession');
}
public function dologin($email,$password)
{
$getData = $this->em()->getRepository('Album\Entity\User')->findOneBy(array('email' => $email, 'password' => $password));
if(count($getData)){
$this->usersession->offsetSet('userid', $getData->getId());
return true;
}
else{
return false;
}
}
public function isloggedin(){
$userid = $this->usersession->offsetGet('userid');
if(!empty($userid)){
return true;
}
else{
return false;
}
}
public function logindata(){
$userid = $this->usersession->offsetGet('userid');
$getData = $this->em()->getRepository('Album\Entity\User')->findOneBy(array('id' => $userid));
return $getData;
}
public function logout(){
$this->usersession->offsetUnset('userid');
}
public function em(){
return $this->entityManager = $this->getController()->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
}
?>
In my module.config.php
'controller_plugins' => array(
'invokables' => array(
'AuthPlugin' => 'Album\Controller\Plugin\AuthenticationPlugin',
)
),
Now I am doing this in my controller:
protected $entityManager;
protected $isloggedin;
protected $authentication;
public function __construct(){
$this->authentication = $this->AuthPlugin();
$this->isloggedin = $this->authentication->isloggedin();
}
The error I am getting is like below:
An error occurred An error occurred during execution; please try again
later. Additional information:
Zend\ServiceManager\Exception\ServiceNotFoundException
File:
D:\xampp\htdocs\subhasis\zf2-tutorial\vendor\zendframework\zendframework\library\Zend\ServiceManager\ServiceManager.php:555
Message:
Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for AuthPlugin
But if I write the above constructor code in any of my controller actions everything is fine. in ZF1 I could initialize any variable in the init() method and could use the variable in any of my actions. How can I do this in ZF2? Here, I want to detect if the user is logged in the constructor itself. Now I have to call the plugin in every action which I don't want.
What should I do here?
The error you are receiving is because you are trying to use the ServiceManager (via the Zend\Mvc\Controller\PluginManager) in the __construct method of the controller.
When a controller is registered as an invokable class, the Service Manager (ControllerManager) is responsible for the creating the controller instance. Once created, it will then call the controllers various default 'initializers' which also inlcudes the plugin manager. By having your code in __construct it is trying to use the plugin manager before it has been set.
You can resolve this by using a controller factory, rather than an invokable in module.config.php.
'controllers' => [
'factories' => [
'MyModule\Controller\Foo' => 'MyModule\Controller\FooControllerFactory',
],
],
Then the factory
namespace MyModule\Controller\FooControllerFactory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class FooControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllerManager)
{
$serviceManager = $controllerManager->getServiceLocator();
$controllerPluginManager = $serviceManager->get('ControllerPluginManager');
$authPlugin = $controllerPluginManager->get('AuthPlugin');
return new FooController($authPlugin);
}
}
Lastly, update the controller __construct to add the new argument and remove the call to $this->authPlugin()
class FooController extends AbstractActionController
{
public function __construct(AuthPlugin $authentication)
{
$this->authentication = $authentication;
$this->isloggedin = $authentication->isloggedin();
}
}
Synopsis
I am building a system with at least two levels of Authentication and both have separate User models and tables in the database. A quick search on google and the only solution thus far is with a MultiAuth package that shoehorns multiple drivers on Auth.
My goal
I am attempting to remove Auth which is fairly straight-forward. But I would like CustomerAuth and AdminAuth using a separate config file as per config/customerauth.php and config\adminauth.php
Solution
I'm assuming you have a package available to work on. My vendor namespace in this example will simply be: Example - all code snippets can be found following the instructions.
I copied config/auth.php to config/customerauth.php and amended the settings accordingly.
I edited the config/app.php and replaced the Illuminate\Auth\AuthServiceProvider with Example\Auth\CustomerAuthServiceProvider.
I edited the config/app.php and replaced the Auth alias with:
'CustomerAuth' => 'Example\Support\Facades\CustomerAuth',
I then implemented the code within the package for example vendor/example/src/. I started with the ServiceProvider: Example/Auth/CustomerAuthServiceProvider.php
<?php namespace Example\Auth;
use Illuminate\Auth\AuthServiceProvider;
use Example\Auth\CustomerAuthManager;
use Example\Auth\SiteGuard;
class CustomerAuthServiceProvider extends AuthServiceProvider
{
public function register()
{
$this->app->alias('customerauth', 'Example\Auth\CustomerAuthManager');
$this->app->alias('customerauth.driver', 'Example\Auth\SiteGuard');
$this->app->alias('customerauth.driver', 'Example\Contracts\Auth\SiteGuard');
parent::register();
}
protected function registerAuthenticator()
{
$this->app->singleton('customerauth', function ($app) {
$app['customerauth.loaded'] = true;
return new CustomerAuthManager($app);
});
$this->app->singleton('customerauth.driver', function ($app) {
return $app['customerauth']->driver();
});
}
protected function registerUserResolver()
{
$this->app->bind('Illuminate\Contracts\Auth\Authenticatable', function ($app) {
return $app['customerauth']->user();
});
}
protected function registerRequestRebindHandler()
{
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function() use ($app) {
return $app['customerauth']->user();
});
});
}
}
Then I implemented: Example/Auth/CustomerAuthManager.php
<?php namespace Example\Auth;
use Illuminate\Auth\AuthManager;
use Illuminate\Auth\EloquentUserProvider;
use Example\Auth\SiteGuard as Guard;
class CustomerAuthManager extends AuthManager
{
protected function callCustomCreator($driver)
{
$custom = parent::callCustomCreator($driver);
if ($custom instanceof Guard) return $custom;
return new Guard($custom, $this->app['session.store']);
}
public function createDatabaseDriver()
{
$provider = $this->createDatabaseProvider();
return new Guard($provider, $this->app['session.store']);
}
protected function createDatabaseProvider()
{
$connection = $this->app['db']->connection();
$table = $this->app['config']['customerauth.table'];
return new DatabaseUserProvider($connection, $this->app['hash'], $table);
}
public function createEloquentDriver()
{
$provider = $this->createEloquentProvider();
return new Guard($provider, $this->app['session.store']);
}
protected function createEloquentProvider()
{
$model = $this->app['config']['customerauth.model'];
return new EloquentUserProvider($this->app['hash'], $model);
}
public function getDefaultDriver()
{
return $this->app['config']['customerauth.driver'];
}
public function setDefaultDriver($name)
{
$this->app['config']['customerauth.driver'] = $name;
}
}
I then implemented Example/Auth/SiteGuard.php (note the methods implemented have an additional site_ defined, this should be different for other Auth drivers):
<?php namespace Example\Auth;
use Illuminate\Auth\Guard;
class SiteGuard extends Guard
{
public function getName()
{
return 'login_site_'.md5(get_class($this));
}
public function getRecallerName()
{
return 'remember_site_'.md5(get_class($this));
}
}
I then implemented Example/Contracts/Auth/SiteGuard.php
use Illuminate\Contracts\Auth\Guard;
interface SiteGuard extends Guard {}
Finally I implemented the Facade; Example/Support/Facades/Auth/CustomerAuth.php
<?php namespace Example\Support\Facades;
class CustomerAuth extends Facade
{
protected static function getFacadeAccessor()
{
return 'customerauth';
}
}
A quick update, when trying to use these custom auth drivers with phpunit you may get the following error:
Driver [CustomerAuth] not supported.
You also need to implement this, the easiest solution is override the be method and also creating a trait similar to it:
<?php namespace Example\Vendor\Testing;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
trait ApplicationTrait
{
public function be(UserContract $user, $driver = null)
{
$this->app['customerauth']->driver($driver)->setUser($user);
}
}
I am upgrading my Laravel application from 4 to 5. However, I have a custom validator that I cannot get to work.
In L4, I made a validators.php file and included it in global.php using require app_path().'/validators.php';.
I tried doing somewhat the same in L5. I dropped a validator in app/Validators/Validators.php, and updated my composer.json.
"files": [
"app/Validators/Validators.php"
]
However, now nothing renders on any page. What've I done wrong?
Try the following:
Make a bind class where you can implement each rule you want extending Validator class.
Make a service provider that extends ServiceProvider.
Add your custom validator provider at config/app.php file.
You can create the bind at Services folder like this:
namespace MyApp\Services;
class Validator extends \Illuminate\Validation\Validator{
public function validateFoo($attribute, $value, $parameters){
return $value == "foo"
}
}
Then, use a service provider to extends the core:
namespace MyApp\Providers;
use MyApp\Services\Validator;
use Illuminate\Support\ServiceProvider;
class ValidatorServiceProvider extends ServiceProvider{
public function boot()
{
\Validator::resolver(function($translator, $data, $rules, $messages)
{
return new Validator($translator, $data, $rules, $messages);
});
}
public function register()
{
}
}
Finally, import your service provider at config/app.php like so:
'providers' => [
...
...
'MyApp\Providers\ValidatorServiceProvider';
]
so here's what I did on adding a custom validation. this is for laravel 5.1
run PHP Artisan make:request MyFormValidationRequest file is created under app\Requests\MyFormValidationRequest.php
Here's the initial code:
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class MyFormValidationRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}
IMPORTANT: Change the return value of authorize() method to true, if you're not doing any authentication. it's initial value is false. else you get a white page with a "Forbidden" error message.
I added a rule under the function rules(), here's what it looks like
public function rules() {
return [
'activeuntil' => 'today_onwards'
];
}
today_onwards is my new validation.
I created a folder named 'Services' under App folder
I created a file named 'ValidatorExtended.php' under App\Services folder , here's the code below:
<?php
namespace App\Services;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
class ValidatorExtended extends Validator {
private $_custom_messages = array(
"today_onwards" => "The :attribute must be today onwards",
);
public function __construct( $translator, $data, $rules, $messages = array(), $customAttributes = array() ) {
parent::__construct( $translator, $data, $rules, $messages, $customAttributes );
$this->_set_custom_stuff();
}
protected function _set_custom_stuff() {
//setup our custom error messages
$this->setCustomMessages( $this->_custom_messages );
}
protected function validateTodayOnwards( $attribute, $value ) {
$now = strtotime('-1 day');
$valueDateFormat = strtotime($value);
if($valueDateFormat > $now){
return true;
}
else {
return false;
}
}
}
Note: the validateTodayOnwards method is where you put your logic.
the name of the method should always start in "validate" then the name of your new validation key which should be in title case,
Another note your validation key should be separated by underscore and all small letters, in this case, "today_onwards". the underscore should be put before all first capital letters in the method name. I hope I explained it good.
TodayOnwards method is equivalent to validation name of "today_onwards",
another example, if I created validateOldPassword, your validation key should be "old_password".
I added below code in app\Providers\AppServiceProvider.php inside boot() method.
Validator::resolver(function($translator, $data, $rules, $messages = array(), $customAttributes = array())
{
return new ValidatorExtended($translator, $data, $rules, $messages, $customAttributes);
});
Don't forget to add below library, one is the Validator class and the other is your own class which is the "ValidatorExtended".
use App\Services\ValidatorExtended;
use Illuminate\Support\Facades\Validator;
Here's what the whole file looks like, [app\Providers\AppServiceProvider.php]
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\ValidatorExtended;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
Validator::resolver(function($translator, $data, $rules, $messages = array(), $customAttributes = array())
{
return new ValidatorExtended($translator, $data, $rules, $messages, $customAttributes);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
That's it. done. you created your own custom validation.
Additionally, if you want to use it in your controller, below is the code:
class testController extends Controller
{
public function updatePass(MiscValidation $request){
//code here
}
}
Instead of using Request Class you use your own class which is an extension of the Request class.
I have installed this package https://github.com/Intervention/image with composer. I have added
'IntImage' => 'Intervention\Image\Facades\Image'
to config/app under aliases
I get the following error and cant figure out what I am doing incorrectly I am sure it has something to do with namespacing /autoloading but app/acme is in the psr-o section of composer.json
'Argument 1 passed to
Acme\Services\Images\InterventionImageEditor::__construct() must be an
instance of IntImage, none given, called in
/var/www/app/ACme/Providers/ImageEditorServiceProvider.php on line 14
and defined' in
/var/www/app/Acme/Services/Images/InterventionImageEditor.php:11
I have the following directory structure
app
acme
Providers
ImageEditorServiceProvider.php
Services
Images
ImageEditorInterface.php
InterventionImageEditor.php
and the content of the files
ImageEditorServiceProvider.php
<?php namespace Acme\Providers;
use Illuminate\Support\ServiceProvider;
use Acme\Services\Images\InterventionImageEditor;
/**
*
*/
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor();
});
}
}
ImageEditorInterface.php
<?php namespace Acme\Services\Images;
interface ImageEditorInterface
{
public function hello();
}
InterventionImageEditor.php
<?php namespace Acme\Services\Images;
use IntImage;
/**
*
*/
class InterventionImageEditor implements ImageEditorInterface
{
protected $imageeditor;
public function __construct(IntImage $imageeditor)
{
$this->imageeditor = $imageeditor;
}
public function hello()
{
$hello = 'hello';
return $hello;
}
}
Can I
Use IntImage;
in this way because it is a facade or am I missing something?
edit to include solution;
changing the service provider to the following resolved the problem
<?php namespace Acme\Providers;
use Illuminate\Support\ServiceProvider;
use Acme\Services\Images\InterventionImageEditor;
use IntImage;
/**
*
*/
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
$intimage = new IntImage;
return new InterventionImageEditor($intimage);
});
}
}
The error is coming from ImageEditorServiceProder.php:
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor();
});
Here you are instantiating the InterventionImageEditor without any parameters. You InterventionImageEditor requires one parameter of type IntImage.
If there are places where you won't have IntImage when instantiating InterventionImageEditor then you need to update your InterventionImageEditor::__construct so that it accepts null(possibly).
function __construct(IntImage $imageeditor = null)
{
if (is_null($imageeditor)) {
// Construct a default imageeditor
// $imageeditor = new ...;
}
$this->imageeditor = $imageeditor;
}
i am not sure you can using IntImage because this file is Facades.
if you want to extending the intervention class. you should add Intervention\Image\Image to your ImageEditorServiceProvider.
use Intervention\Image\Image;
class ImageEditorServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Acme\Services\Images\ImageEditorInterface', function () {
return new InterventionImageEditor(new Image);
});
}
}