Giter VIP home page Giter VIP logo

laravel-heyman's Introduction

Laravel HeyMan

Readability Counts. In fact, Readability is the primary value of your code !!!

Quality Score code coverage Maintainability Imports Test StyleCI Latest Stable Version Daily Downloads Total Downloads Awesome Laravel Software License

๐ŸŽ€ Heyman continues where the other role-permission packages left off...

We have used CDD (Creativity Driven Development) alongside the TDD

Built with โค๏ธ for every smart laravel developer

Very well-tested, optimized, and production-ready!

We have tackled a lot of complexity behind the scenes, to provide you with a lot of simplicity.

Installation:


composer require imanghafoori/laravel-heyman

Requirements:

  • PHP v7.0 or above
  • Laravel v5.1 or above

Example:

Here you can see a good example at:

https://github.com/imanghafoori1/council

Specially this file:

https://github.com/imanghafoori1/council/blob/master/app/Providers/AuthServiceProvider.php

This is fork from result of laracasts.com tutorial series refactored to use the Heyman package.

Heyman, let's fight off zombies

Zombie Http Request =>


<= Laravel Heyman





A story:

Imagine your boss comes to you and says :

 Hey man !!!
 
 When you visit the login form,
 
 You should be guest,
 
 Otherwise you get redirected to '/panel',

Write the code for me, just now... But KEEP IN MIND you are not allowed to touch the current code. it is very sensitive and we do not want you to tamper with it. You may break it.

And you write code like this in a Service Provider boot method to implement what your boss wanted.

image

That is what this package does for you + a lot more...

Customizable Syntax:

You can alias methods like this if you do not like too much verbose syntax provided by default.

  • Alias Situations (ex. whenYouMakeView to view)
  • Alias Conditions (ex. youShouldBeGuest to beGuest)

You should do it in the boot method.

alias methods

Structural Benefits:

1- This way you can fully decouple authorization and a lot of guarding code from the rest of your application code and put it in an other place. So your Controllers and Routes become less crowded and you will have a central place where you limit the access of users to your application or perform Request validation.

2- In fact, when you write your code in the way, you are conforming to the famous "Tell don't ask principle."

You are telling the framework what to do in certain situations rather than getting information and decide what to do then.

Procedural code gets information then makes decisions. Object-oriented code tells objects to do things. โ€” Alec Sharp

3- This approach is particularly useful when you for example write a package which needs ACL but you want to allow your package users to override and apply they own ACL (or validation) rules into your package routes...

And that becomes possible when you use laravel-HeyMan for ACL. The users can easily cancel out the default rules and re-write their favorite acl or validation stuff in a regular ServiceProviders.

Hey Man, that is Amazing stuff!

// This is written in package and lives in vendor folder, So we can not touch it.
HeyMan::whenYouHitRouteName('myPackageRoute')->youShouldHaveRole(....; 

To override that we use the forget method, within app/Providers/... :

public function boot() {
  
  // Cancels out the current rules
   HeyMan::forget()->aboutRoute('myPackageRoute');
  
  
   // Add new rules by package user.
   HeyMan::whenYouHitRouteName('myPackageRoute')-> ... 
   
}

Hey Man, Should I Momorize all the Methods?!

You do not need any cheat sheet.

IDE Auto-completion is fully supported.

refactor5

Hey Man, Where do I put these Heyman:: calls?

You may put them in AuthServiceProvider.php (or any other service provider) boot method.

image

Usage:

You should call the following method of the HeyMan Facade class.

use Imanghafoori\HeyMan\Facades\HeyMan;
// or
use HeyMan;  // <--- alias

Again we recommend visiting this file:

Working heyman sample rules

Situations:

HeyMan::  (situation) ->   (condition)   -> otherwise() -> (reaction) ;

1- Url is matched

HeyMan::whenYouVisitUrl(['/welcome', '/home'])->...   // you can pass an Array
HeyMan::whenYouVisitUrl('/admin/*')->...     // or match by wildcard
HeyMan::whenYouSendPost('/article/store')->   ...   
HeyMan::whenYouSendPatch('/article/edit')->  ...  
HeyMan::whenYouSendPut('/article/edit')->    ...     
HeyMan::whenYouSendDelete('/article/delete')-> ...

2- Route Name is matched

HeyMan::whenYouHitRouteName('welcome.name')->...              // For route names
HeyMan::whenYouHitRouteName('welcome.*')->...                 // or match by wildcard

3- Controller Action is about to Call

HeyMan::whenYouCallAction('HomeController@index')->...
HeyMan::whenYouCallAction('HomeController@*')->...          // or match by wildcard

4- A View file is about to render

 HeyMan::whenYouMakeView('article.editForm')->...     // also accepts an array
 HeyMan::whenYouMakeView('article.*')->...            // You can watch a group of views

Actually it refers to the moment when view('article.editForm') is executed.

5- Custom Event is Fired

HeyMan::whenEventHappens('myEvent')->...

Actually it refers to the moment when event('myEvent') is executed.

6- An Eloquent Model is about to save

HeyMan::whenYouSave(\App\User::class)->...
HeyMan::whenYouFetch(\App\User::class)->...
HeyMan::whenYouCreate(\App\User::class)->...
HeyMan::whenYouUpdate(\App\User::class)->...
HeyMan::whenYouDelete(\App\User::class)->...

Actually it refers to the moment when eloquent fires it's internal events like: (saving, deleting, creating, ...)

Note that the saving model is passed to the Gate of callback in the next chain call. so for example you can check the ID of the model which is saving.

Conditions:

HeyMan::  (situation) ->   (condition)   -> otherwise() -> (reaction) ;

After mentioning the situation, it is time to mention the condition.

1- Gates:

// define Gate
Gate::define('hasRole', function(){...});

Then you can use the gate:

HeyMan::whenYouVisitUrl('/home')->thisGateShouldAllow('hasRole', 'editor')->otherwise()->...;

Passing a Closure as a Gate:

$gate = function($user, $role) {
    /// some logic
    return true;
}
HeyMan::whenYouVisitUrl('/home')->thisGateShouldAllow($gate, 'editor')->otherwise()->...;

2- Authentication stuff:

HeyMan::whenYouVisitUrl('/home')->  youShouldBeGuest()    ->otherwise()->...;
HeyMan::whenYouVisitUrl('/home')->  youShouldBeLoggedIn() ->otherwise()->...;

3- Checking A Closure or Method or Value:

HeyMan::whenYouVisitUrl('home')->thisMethodShouldAllow('someClass@someMethod', ['param1' => 'value1'])->otherwise()->...;
HeyMan::whenYouVisitUrl('home')->thisClosureShouldAllow( function($a) { ... }, ['param1'] )  ->otherwise()->...;
HeyMan::whenYouVisitUrl('home')->thisValueShouldAllow( $someValue )->otherwise()->...;

4- Validate Requests:

HeyMan::whenYouHitRouteName('articles.store')->yourRequestShouldBeValid([
    'title' => 'required', 'body' => 'required',
]);

You can also modify the data before validation by calling beforeValidationModifyData().

$modifier = function ($data) {
  // removes "@" character from the "name" before validation.
  $data['name'] = str_replace('@', '', $data['name']);
  return $data;
}

HeyMan::whenYouHitRouteName('welcome.name')
        ->yourRequestShouldBeValid(['name' => 'required'])
        ->beforeValidationModifyData($modifier);

5- Check points:

You can also declare some check points somewhere, within your application code:

HeyMan::checkPoint('MyLane');

And put some rules for it

HeyMan::whenYouReachCheckPoint('MyLane')->youShouldHaveRole('Zombie')-> ...
HeyMan::whenYouVisitUrl('home')->always()-> ...
HeyMan::whenYouVisitUrl('home')->sessionShouldHave('key1')->...

Other things:

You can also use "always" and "sessionShouldHave" methods:

HeyMan::whenYouVisitUrl('home')->always()-> ...
HeyMan::whenYouVisitUrl('home')->sessionShouldHave('key1')->...

Define your own conditions:

You can extend the conditions and introduce new methods into heyman API like this:

// Place this code:
// In the `boot` method of your service providers

HeyMan::condition('youShouldBeMan', function () {
   return function () {
       return auth()->user() && auth()->user()->gender === 'Man';
   };
});

// or 

HeyMan::condition('youShouldBeMan', '\App\SomeWhere\SomeClass@someMethod');

Then you can use it like this:

HeyMan::whenYouVisitUrl('home')->youShouldBeMan()-> ...

Nice, isn't it ?!

Reactions:

HeyMan::  (situation) ->   (condition)   -> otherwise() -> (reaction) ;

1- Deny Access:

HeyMan::whenSaving(\App\User::class)->thisGateShouldAllow('hasRole', 'editor')->otherwise()->weDenyAccess();

An AuthorizationException will be thrown if needed

2- Redirect:

HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->redirect()->to(...)     ->with([...]);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->redirect()->route(...)  ->withErrors(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->redirect()->action(...) ->withInput(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->redirect()->intended(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->redirect()->guest(...);

In fact the redirect method here is very much like the laravel's redirect() helper function.

3- Throw Exception:

$msg = 'My Message';

HeyMan::whenYouVisitUrl('/login')
    ->youShouldBeGuest()
    ->otherwise()
    ->weThrowNew(AuthorizationException::class, $msg);

4- Abort:

HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->abort(...);

5- Send Response:

Calling these functions generate exact same response as calling them on the response() helper function: return response()->json(...);

HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->response()->json(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->response()->view(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->response()->jsonp(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->response()->make(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->response()->download(...);

6- Send custom response:

HeyMan::whenYouVisitUrl('/login')-> 
       ...
      ->otherwise()
      ->weRespondFrom('\App\Http\Responses\Authentication@guestsOnly');
namespace App\Http\Responses;

class Authentication
{
    public function guestsOnly()
    {
        if (request()->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }

        return redirect()->guest(route('login'));
    }
}

Hey man, You see ? we have just an Http response here. So our controllers are free to handle the right situaltions and do not worry about exceptional ones.

More Advanced Reactions:

Hey man, You may want to call some method or fire an event right before you send the response back. You can do so by afterCalling() and afterFiringEvent() methods.

HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->afterFiringEvent('explode')->response()->json(...);
HeyMan::whenYouVisitUrl('/login')-> ... ->otherwise()->afterCalling('someclass@method1')->response()->json(...);

Disabling Heyman:

You can disable HeyMan checks like this (useful while testing):

untitled

HeyMan::turnOff()->eloquentChecks();

...
/// You may save some eloquent models here...
/// without limitations from HeyMan rules.
...

HeyMan::turnOn()->eloquentChecks();

๐Ÿ™‹ Contributing:

If you find an issue, or have a better way to do something, feel free to open an issue or a pull request.

โญ Your Stars Make Us Do More โญ

As always if you found this package useful and you want to encourage us to maintain and work on it. Just press the star button to declare your willing.

Star History

Star History Chart

More from the author:

Laravel Widgetize

๐Ÿ’Ž A minimal yet powerful package to give a better structure and caching opportunity for your laravel apps.


Laravel Terminator

๐Ÿ’Ž A minimal yet powerful package to give you opportunity to refactor your controllers.


Laravel AnyPass

๐Ÿ’Ž It allows you login with any password in local environment only.


Laravel Microscope

๐Ÿ’Ž It automatically checks your laravel application (new)


Great spirits have always encountered violent opposition from mediocre minds.

"Albert Einstein"

laravel-heyman's People

Contributors

amirsadeghi1 avatar dependabot[bot] avatar i-iman-i avatar imanghafoori1 avatar laravel-shift avatar mehradsadeghi avatar stylecibot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-heyman's Issues

Out of memory error

This problem happened to me several times in the past but I did not report it thinking it could be several things.
But after encountering this problems several times during installing laravel-heyman, I try to reproduce the error.

After installing new laravel project with "laravel new name"
then run "composer require imanghafoori/laravel-heyman"
I got the following error.

composer require imanghafoori/laravel-heyman
Using version ^2.2 for imanghafoori/laravel-heyman
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
PHP Fatal error:  Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/DependencyResolver/Solver.php on line 223

Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/DependencyResolver/Solver.php on line 223

Check https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors for more info on how to handle out of memory errors.

Environment: Windows 10, PHP 7.4, composer 1.10, laravel installer 4.0

Improvement suggestion

Hi, i'm really in love with what you did and the fact that, it doesn't necessarily need to be added to an existing code (Controller) makes it more flexible.

However, i have an issue with the spell termination. If you say "whenDoingThis", this assume the action has already taken place, and some might not understand that "whenDoingThis" is occuring before.

So i would like to suggest renaming "when.." to "before...". This can increase the possibility because it will be possible to do something like "afterDoingThis".

Anyway, good job. I might use it on my upcoming projects.

Some grouped suggestions

New Method : AssertThat( boolean )

As what i can see, the library is closely linked to plain Laravel installation. However, many out there already have a laravel application with their superset of functions (classes). So, we might introduce an asset true (or assertThat) method, which give the developper the capacity to to his own assetion and proceed (or not) the even.

Example :

use App\Factory\Post as PostFactory;
// ...
HeyMan::whenYouDelete( App\Models\Post::class )
->assertThat( PostFactory::OnePostLeft() )
// ...

New Method : redirectTo()

Having to write down ->redirect()->to()... makes the chain more longer. We might implement a method like ->redirectTo() which join both methods.

New Method : otherwiseThrow( \Exception )

Well, we're handling exception in a different ways on our laravel application. I assume many might have a different view (message) for different exceptions. It might be interesting on the chain to throw an exception.
Edit : i've seen that there is a Exception method, we might then consider reducing the chain.

Example :

use App\Guards\HeavenGuard;
use Illuminate\Support\Auth;
use App\Exceptions\NotForYouException;
// ...
HeyMan::whenYouVisitUrl(  '/paradise'  )
->assertThat( HeavenGuard::isBlessed( Auth::user() ) ) // new method
->otherwiseThrow( NotForYouException::class )
// ...

New method : whenYouVisiteRoute()

instead of typing down the URL with slashes, we might have a route support method.

cannot install the package

I'm using PHP 7.3.8 and Laravel 7
I got the following error when trying to install the package.

Using version ^2.2 for imanghafoori/laravel-heyman
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)

Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar:///usr/bin/composer/src/Composer/DependencyResolver/Solver.php on line 223

Any suggestion is appreciated.

Declarative style in Testing

It is not an issue, it is more a feature, but I would like to use this declarative style in my tests, do you think it is possible to do this?

HeyMan and Laravel policy

Is there a way to use HeyMan together with Laravel policy?
I only found an example for Gate but no policy anywhere.

Thanks in advance.

Error with db:seed

When I make the command db: seed I get the following error: 'Undefined index: eloquentChecks'

laravel 10 compatibility issue

Error on require in laravel v10

Using version ^2.2 for imanghafoori/laravel-heyman
./composer.json has been updated
Running composer update imanghafoori/laravel-heyman
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

Problem 1
- imanghafoori/laravel-heyman[2.2.0, ..., 2.2.7] require laravel/framework ~5.3 -> found laravel/framework[v5.3.0, ..., v5.8.38] but it conflicts with your root composer.json require (^10.0).
- imanghafoori/laravel-heyman[2.2.8, ..., v2.2.34] require laravel/framework ~5.1 -> found laravel/framework[v5.1.0, ..., v5.8.38] but it conflicts with your root composer.json require (^10.0).
- imanghafoori/laravel-heyman[v2.2.35, ..., v2.2.43] require laravel/framework ~5.1|6.* -> found laravel/framework[v5.1.0, ..., v5.8.38, v6.0.0, ..., v6.20.44] but it conflicts with your root composer.json require (^10.0).
- imanghafoori/laravel-heyman[v2.2.44, ..., v2.2.45] require laravel/framework ~5.1|6.|7. -> found laravel/framework[v5.1.0, ..., v5.8.38, v6.0.0, ..., v6.20.44, v7.0.0, ..., v7.30.6] but it conflicts with your root composer.json require (^10.0).
- imanghafoori/laravel-heyman[v2.2.46, ..., v2.2.52] require laravel/framework ~5.1|6.|7.|8.* -> found laravel/framework[v5.1.0, ..., v5.8.38, v6.0.0, ..., v6.20.44, v7.0.0, ..., v7.30.6, v8.0.0, ..., v8.83.27] but it conflicts with your root composer.json require (^10.0).
- imanghafoori/laravel-heyman[v2.2.53, ..., v2.2.54] require laravel/framework ~5.1|6.|7.|8.|9. -> found laravel/framework[v5.1.0, ..., v5.8.38, v6.0.0, ..., v6.20.44, v7.0.0, ..., v7.30.6, v8.0.0, ..., v8.83.27, v9.0.0, ..., v9.52.0] but it conflicts with your root composer.json require (^10.0).
- Root composer.json requires imanghafoori/laravel-heyman ^2.2 -> satisfiable by imanghafoori/laravel-heyman[2.2.0, ..., v2.2.54].

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.
You can also try re-running composer require with an explicit version constraint, e.g. "composer require imanghafoori/laravel-heyman:*" to figure out if any version is installable, or "composer require imanghafoori/laravel-heyman:^2.1" if you know which you need.

Installation failed, reverting ./composer.json and ./composer.lock to their original content.

How can I use whenYouCallAction() with custom controllers namespace?

Dear @imanghafoori1, I am very attracted to use this package on my new project. But have a question since I use Dingo/Api package with this.

I want do something like bellow:

HeyMan::
whenYouCallAction('App\Api\V1\Controllers\SomeController@action')
...

I saw this line where you hard code the controllers' namespace

return app()->getNamespace().'\\Http\\Controllers\\'.$action;
my question is; how can I use my own namespace on whenYouCallAction function?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.