Giter Site home page Giter Site logo

hookable's Introduction

Sofa/Hookable

GitHub Tests Action Status stable Downloads

Hooks system for the Eloquent ORM (Laravel 5.2).

Hooks are available for the following methods:

  • Model::getAttribute
  • Model::setAttribute
  • Model::save
  • Model::toArray
  • Model::replicate
  • Model::isDirty
  • Model::__isset
  • Model::__unset

and all methods available on the Illuminate\Database\Eloquent\Builder class.

Installation

Clone the repo or pull as composer dependency:

composer require sofa/hookable:~5.2

Usage

In order to register a hook you use static method hook on the model: example.

Important Due to the fact that PHP will not let you bind a Closure to your model's instance if it is created in a static context (for example model's boot method), you need to hack it a little bit, in that the closure is created in an object context.

For example see the above example along with the class that encloses our closures in an instance scope that is being used there.

Signature for the hook closure is following:

function (Closure $next, mixed $payload, Sofa\Hookable\Contracts\ArgumentBag $args)

Hooks are resolved via Sofa\Hookable\Pipeline in the same order they were registered (except for setAttribute where the order is reversed), and each is called unless you return early:

// example hook on getAttribute method:
function ($next, $value, $args)
{
    if (/* your condition */) {
        // return early
        return 'some value'; // or the $value
    }

    else if (/* other condition */) {
        // you may want to mutate the value
        $value = strtolower($value);
    }

    // finally continue calling other hooks
    return $next($value, $args);
}

Contribution

All contributions are welcome, PRs must be tested and PSR-2 compliant.

hookable's People

Contributors

arnaudlier avatar boukeversteegh avatar erictendian avatar itsjavi avatar jarektkaczyk avatar jonruttan avatar kajetan-nobel avatar remo avatar salkhwlani 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

Watchers

 avatar  avatar  avatar

hookable's Issues

"count" method returns string number with Amazon Aurora.

Dear jarektkaczyk,

I greatly appreciate your works(hookable, eloquence, etc...), they are so helpful.
But I found one minor issue that the method "count()" in "Sofa\Hookable\Builder" returns string number with Amazon Aurora database.
It works well with MySQL on the local machine.
I think you should cast the result returned from "aggregate()" as Eloquent does.

return (int) $this->aggregate(__FUNCTION__, (array) $columns);

Would you consider my suggestion?

Best regards.

Add hooks for model relations

Hi,

I'd love to see some hooks for proper relations mapping (i. e. map foreign keys) in combination with your Eloquence package.
Would you accept pull requests here to add hooks for hasOne, belongsTo, hasMany etc?

Check number of parameters passed to where function

Step to reproduce the problem: pass only 2 parameters to where method, the second one must be a string containing a valid SQL operator. Something like this:
$model->where('id', '<>');
This will make a query like this:
select * from table where id is not null
Where method should check the number of parameters passed and pack inside the $bag only the ones passed by the user, as eloquent already have default values for those how are missing, or skip the first check that verify if $operator is a valid SQL operator if there are only 2 parameters.

Conflict with laratrust

There's a conflict with laratrust because this library overrides setAttribute and doesn't return the instance of the model ($this) at the end. I'm not quite sure if we can easily change this without breaking something.

I've also posted an issue here santigarcor/laratrust#314

Hooks are being duplicated from child classes

It seems that hooks are being duplicated in the pipeline for models that extend a base class that includes the Eloquence traits. As an example creating a base model and including eloquence, mappable, and mutable, end up adding into the pipeline 2 mappable hooks and 2 mutable hooks.

This ends up some how entering an infinite loop. This seems to also only happen when a large number of models using both mappable and mutable are all related and used en masse.

Declaration of Sofa\Hookable\Builder::pluck() should be compatible with Illuminate\Database\Eloquent\Builder::pluck($column, $key = NULL)

Hi

I'm seeing this error when using the library in Laravel 5.2

local.ERROR: exception 'ErrorException' with message 'Declaration of Sofa\Hookable\Builder::pluck() should be compatible with Illuminate\Database\Eloquent\Builder::pluck($column, $key = NULL)' in /PATH/vendor/sofa/hookable/src/Builder.php:13

the simple solution seems to be to alter the method declaration and add the $key = NULL as a parameter so that it matches the required signature of Illuminate\Database\Eloquent\Builder.

I can submit a pull request to introduce this change but are you considering 5.2 compatibility at this time? and if so are you wanting to keep 5.1 compatibility?

I'm interested in this for making use of your eloquence package so might need to do some changes to that for 5.2 as well.

Thanks

Mark

Aggregates don't work correctly (anymore?)

In the current Builder instance in this package, the aggregate method doesn't remove things like order at least like the latest version does to remove errors around full group by.

I can run the same query twice using this package enabled and without and the resulting queries look like:

With:

select count(*) as aggregate from "manufacturers" order by "id" desc

Without:

SELECT count(*) as aggregate FROM "manufacturers"

This causes the query to fail on servers where this option is enabled, whereas base installs of laravel, can function properly with this option.

For reference, https://github.com/laravel/framework/blob/master/src/Illuminate/Database/Query/Builder.php#L2421

"shared static" causes BadMethodCallException on Builder.

This problem happens while using Eloquence but it seems to be Hookable related, so I'm reporting it here.

I'm running this on PHP 7.0.12.

I have my own base model class where I add the Eloquence trait, so I'm not using the provided Model.

For the purpose of this I have 2 classes:

Category (use Mappable) <-- MyModel (use Eloquence)
User <-- MyModel (use Eloquence)

When the Category is booted, it receives the hooks for Mappable as it should.
However, when after that I use the User model, I get an error Call to undefined method Sofa\Eloquence\Query\Builder::hasMapping()

While debugging I noticed that User::setAttribute('first_name', ... gets a call to /sofa/eloquence/src/Mappable/Hooks.php:127 in the pipeline.

Further debugging turned up that the User does indeed have a bound hook to the Mappable trait, as if the static defined in the Hookable trait is shared between the classes.

Dunno if this is something PHP specific but I was under the impression that statics in a trait resulted in separate instances per class, but this insinuates differently...

The stack trace for your viewing pleasure:

3) UserTest::testThatAnOldPasswordIsUpdatedOnLogin
BadMethodCallException: Call to undefined method Sofa\Eloquence\Query\Builder::hasMapping()

/app/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:2450
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:1454
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:3524
/app/vendor/sofa/eloquence/src/Mappable/Hooks.php:127
/app/vendor/sofa/hookable/src/Pipeline.php:84
/app/vendor/sofa/hookable/src/Pipeline.php:88
/app/vendor/sofa/hookable/src/Hookable.php:245
/app/vendor/sofa/hookable/src/Hookable.php:114
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:443
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:281
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:168
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:2280
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:170
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:139
/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php:113
/app/tests/unit/packages/users/UserTest.php:24

strtolower() expects parameter 1 to be string, object given

Hi all,

I wanted to use the searchable function of eloquence, works fine for two models, but if I add the "use Eloquence;" trait to my third model ("Project", not that different form the others) my application throws the following error on a few pages:

strtolower() expects parameter 1 to be string, object given

thrown by:

if (!in_array(strtolower($operator), $this->operators, true)) {

I don't know if that are enough information, but maybe someone has an idea.

-> also opened in jarektkaczyk/eloquence#89

Regards,
Timo

Callstack:

ErrorException in Builder.php line 127:
strtolower() expects parameter 1 to be string, object given
in /home/ubuntu/workspace/vendor/sofa/hookable/src/Builder.php line 127
at HandleExceptions->handleError('2', 'strtolower() expects parameter 1 to be string, object given', '/home/ubuntu/workspace/vendor/sofa/hookable/src/Builder.php', '127', array('column' => 'id', 'operator' => object(Closure), 'value' => null, 'boolean' => 'and'))
at strtolower(object(Closure)) in Builder.php line 127
at Builder->where('id', object(Closure))
at call_user_func_array(array(object(Builder), 'where'), array('id', object(Closure))) in Model.php line 3493
at Model->__call('where', array('id', object(Closure)))
at Project->where('id', object(Closure))
at call_user_func_array(array(object(Project), 'where'), array('id', object(Closure))) in Model.php line 3507
at Model::__callStatic('where', array('id', object(Closure))) in Task.php line 72
at Project::where('id', object(Closure)) in Task.php line 72
at Task->project() in Task.php line 142
at Task->checkUserAccess(object(User)) in TaskController.php line 88
at TaskController->show(object(Request), '1')
at call_user_func_array(array(object(TaskController), 'show'), array(object(Request), 'id' => '1')) in Controller.php line 80
at Controller->callAction('show', array(object(Request), 'id' => '1')) in ControllerDispatcher.php line 146
at ControllerDispatcher->call(object(TaskController), object(Route), 'show') in ControllerDispatcher.php line 94
at ControllerDispatcher->Illuminate\Routing{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 52
at Pipeline->Illuminate\Routing{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 103
at Pipeline->then(object(Closure)) in ControllerDispatcher.php line 96
at ControllerDispatcher->callWithinStack(object(TaskController), object(Route), object(Request), 'show') in ControllerDispatcher.php line 54
at ControllerDispatcher->dispatch(object(Route), object(Request), 'App\Http\Controllers\TaskController', 'show') in Route.php line 174
at Route->runController(object(Request)) in Route.php line 140
at Route->run(object(Request)) in Router.php line 724
at Router->Illuminate\Routing{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 52
at Pipeline->Illuminate\Routing{closure}(object(Request)) in Authenticate.php line 28
at Authenticate->handle(object(Request), object(Closure))
at call_user_func_array(array(object(Authenticate), 'handle'), array(object(Request), object(Closure))) in Pipeline.php line 136

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.