The fastest way to make a powerful JSON:API compatible Rest API with Laravel.

Overview

Build Status Total Downloads Latest Stable Version License

The first fully customizable Laravel JSON:API builder. "CRUD" and protect your resources with 0 (zero) extra line of code.

Installation

You can install the package via composer:

composer require binaryk/laravel-restify

Videos

If you are a visual learner, checkout our video course for the Laravel Restify.

Quick start

Setup package:

php artisan restify:setup

Generate repository:

php artisan restify:repository Dream --all

Now you have the REST CRUD over dreams and this beautiful repository:

Now you can go into Postman and check it out:

GET: http://laravel.test/api/restify/dreams
POST: http://laravel.test/api/restify/dreams
GET: http://laravel.test/api/restify/dreams/1
PUT: http://laravel.test/api/restify/dreams/1
DELETE: http://laravel.test/api/restify/dreams/1

Usage

See the official documentation.

Testing

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Comments
  • Is this package JSON API compatible?

    Is this package JSON API compatible?

    In the documentation you have mentioned:

    JSON:API consistency
    
    Maintain your API consistent accordingly with https://jsonapi.org
    

    But it seems like it isn't.

    I love the way you have structured this package it is close to Laravel Nova.

    opened by martianatwork 5
  • GET /users is always public - it ignores the auth:sanctum middleware

    GET /users is always public - it ignores the auth:sanctum middleware

    I've just started using this package two days ago and I was reading docs carefully and all over again to figure it out why the /users endpoint can be accessed by an unauthenticated user!?

    I've set auth:sanctum under the middleware in the restify.php config file and I've checked other endpoints and they do returns the "Unauthenticated" sanctum message as I expected.

    image

    Now, if I change the method to POST - sanctum works!

    image

    So, I've created a new repository Service just to discover that the same thing happens. So, each index action is publicly accessible and ignores the auth:sanctum middleware for some reason.

    Bonus: it should be --all instead of --app, right? Sub-bonus: Edit this page on GitHub returns 404

    opened by Shonetow 3
  • Boolean Match not triggered when falsy value is sent

    Boolean Match not triggered when falsy value is sent

    I have the following setup

    // Repository
    public static function matches(): array
    {
        return [
            ...
            'has_direct_deposits' => HasDirectDepositsMatch::make()
                    ->setType('boolean'),
            ...
        ];
    }
    
    // HavingDeductionRatesMatch
    <?php
    
    use Binaryk\LaravelRestify\Filters\MatchFilter;
    use Illuminate\Http\Request;
    
    class HasDirectDepositsMatch extends MatchFilter
    {
        public function filter(Request $request, $query, $value)
        {
            dump('x', $request->all());
            if ($request->boolean('has_direct_deposits')) {
                $query->has('directDeposits');
            } else {
                $query->doesntHave('directDeposits');
            }
        }
    }
    
    // Repository Helper
    public static function route(
        string|Model $path = null,
        Action $action = null,
        array $query = [],
    ): string {
        if ($path instanceof Model) {
            $path = $path->getKey();
        }
    
        if ($action) {
            $query['action'] = $action->uriKey();
        }
    
        $route = implode('/', array_filter([
            Restify::path(),
            static::uriKey(),
            $path,
            $action ? 'actions' : null,
        ]));
    
        if (empty($query)) {
            return $route;
        }
    
        return $route.'?'.http_build_query($query);
    }
    

    The query is generated using http_build_query method and converts bools to ints

    Test

    $this->getJson(EmployeeRepository::route(query: ['has_direct_deposits' => true]));
    // Route is /api/restify/employees?has_direct_deposits=1
    // Works as expected, the match is triggered
    
    // Dump
    ^ "x"
    ^ array:1 [
      "has_direct_deposits" => "1"
    ]
    
    $this->getJson(EmployeeRepository::route(query: ['has_direct_deposits' => false]));
    // Route is /api/restify/employees?has_direct_deposits=0
    // The match is not being triggered by Restify
    
    // Nothing is dumped
    
    $this->getJson(EmployeeRepository::route(query: ['has_direct_deposits' => 'false']));
    // Route is /api/restify/employees?has_direct_deposits=false
    // Works as expected, the match is triggered
    
    // Dump
    ^ "x"
    ^ array:1 [
      "has_direct_deposits" => "false"
    ]
    
    opened by arthurkirkosa 2
  • Exception Handler left over on docs

    Exception Handler left over on docs

    Commit https://github.com/BinarCode/laravel-restify/commit/a616a501eaf522c491eb124e45a62b7b2b890e33#diff-f55db4bc294bbb90108ccfcbdd11881f4707473ad9384b382e2a6b653e118471 removed exception_handler configuration, but this docs reference https://restify.binarcode.com/quickstart#exception-handling is left

    edit: this too https://restify.binarcode.com/api/exception-handler#disable-restify-exception-handler

    opened by GeoSot 2
  • Set type on Matches

    Set type on Matches

    We need a way to set the type for class based matches.

    <?php
    
    namespace App\Restify\Matchers;
    
    use Binaryk\LaravelRestify\Repositories\Matchable;
    use Illuminate\Database\Eloquent\Builder;
    use Illuminate\Http\Request;
    
    class SentButNotDownloadedMatch implements Matchable
    {
        public $type = 'boolean';
    
        public function handle(Request $request, Builder $query)
        {
            if ($request->boolean('sent_but_not_downloaded')) {
                $query->whereNotNull('sent_at')
                    ->whereNull('downloaded_at');
            }
        }
    }
    

    Currently this does nothing

    opened by arthurkirkosa 2
  • Remove Email Exist validation

    Remove Email Exist validation

    In case someone is going to guess a bunch of e-mail adresses you don't want to tell them the e-mail adress does exists but the combination is incorrect.

    opened by kevinvanboeckholtz 1
  • Unauthorized to view repository products. Check \

    Unauthorized to view repository products. Check \"allowRestify\" policy.

    https://domain.test/api/restify/products/2 <- Working https://domain.test/api/restify/products <- not working when i am trying to access with id than it's working but when trying without with id like ( api/restify/products) then response is 401 forbidden

    { "message": "Unauthorized to view repository products. Check \"allowRestify\" policy.", "exception": "Symfony\\Component\\HttpKernel\\Exception\\HttpException", "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Application.php", "line": 1148, "trace": [ { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php", "line": 44, "function": "abort", "class": "Illuminate\\Foundation\\Application", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/binaryk/laravel-restify/src/Http/Requests/Concerns/InteractWithRepositories.php", "line": 59, "function": "abort" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/binaryk/laravel-restify/src/Http/Controllers/RepositoryIndexController.php", "line": 12, "function": "repository", "class": "Binaryk\\LaravelRestify\\Http\\Requests\\RestifyRequest", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Controller.php", "line": 54, "function": "__invoke", "class": "Binaryk\\LaravelRestify\\Http\\Controllers\\RepositoryIndexController", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php", "line": 45, "function": "callAction", "class": "Illuminate\\Routing\\Controller", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Route.php", "line": 261, "function": "dispatch", "class": "Illuminate\\Routing\\ControllerDispatcher", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Route.php", "line": 204, "function": "runController", "class": "Illuminate\\Routing\\Route", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 725, "function": "run", "class": "Illuminate\\Routing\\Route", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 141, "function": "Illuminate\\Routing\\{closure}", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/binaryk/laravel-restify/src/Http/Middleware/AuthorizeRestify.php", "line": 22, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Binaryk\\LaravelRestify\\Http\\Middleware\\AuthorizeRestify", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/binaryk/laravel-restify/src/Http/Middleware/DispatchRestifyStartingEvent.php", "line": 29, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Binaryk\\LaravelRestify\\Http\\Middleware\\DispatchRestifyStartingEvent", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/binaryk/laravel-restify/src/Http/Middleware/EnsureJsonApiHeaderMiddleware.php", "line": 20, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Binaryk\\LaravelRestify\\Http\\Middleware\\EnsureJsonApiHeaderMiddleware", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php", "line": 50, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Routing\\Middleware\\SubstituteBindings", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php", "line": 126, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php", "line": 102, "function": "handleRequest", "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php", "line": 54, "function": "handleRequestUsingNamedLimiter", "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Routing\\Middleware\\ThrottleRequests", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 116, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 726, "function": "then", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 703, "function": "runRouteWithinStack", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 667, "function": "runRoute", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 656, "function": "dispatchToRoute", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php", "line": 167, "function": "dispatch", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 141, "function": "Illuminate\\Foundation\\Http\\{closure}", "class": "Illuminate\\Foundation\\Http\\Kernel", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/binaryk/laravel-restify/src/Http/Middleware/RestifyInjector.php", "line": 15, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Binaryk\\LaravelRestify\\Http\\Middleware\\RestifyInjector", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/livewire/livewire/src/DisableBrowserCache.php", "line": 19, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Livewire\\DisableBrowserCache", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/nova/src/Http/Middleware/ServeNova.php", "line": 23, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Laravel\\Nova\\Http\\Middleware\\ServeNova", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php", "line": 21, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php", "line": 31, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php", "line": 21, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php", "line": 40, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php", "line": 27, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php", "line": 86, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php", "line": 62, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Http\\Middleware\\HandleCors", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php", "line": 39, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 180, "function": "handle", "class": "Illuminate\\Http\\Middleware\\TrustProxies", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 116, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php", "line": 142, "function": "then", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php", "line": 111, "function": "sendRequestThroughRouter", "class": "Illuminate\\Foundation\\Http\\Kernel", "type": "->" }, { "file": "/Users/proxgrewal/Documents/Galaxy/public/index.php", "line": 52, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Kernel", "type": "->" }, { "file": "/Users/proxgrewal/.composer/vendor/laravel/valet/server.php", "line": 234, "function": "require" } ] }

    opened by proxAdmin 1
  • fix scout when search input is null

    fix scout when search input is null

    Fix when using ElasticSearch: Elastic\Elasticsearch\Exception\ClientResponseException: 400 Bad Request: {"error":{"root_cause":[{"type":"parsing_exception","reason":"[query_string] unknown token [VALUE_NULL] after [query]","line":1,"col":35}],"type":"parsing_exception","reason":"[query_string] unknown token [VALUE_NULL] after [query]","line":1,"col":35},"status":400}

    opened by ttungbmt 1
  • Store on a repository ignores the $fillable on the Model

    Store on a repository ignores the $fillable on the Model

    Laravel version 8.83.14 Restify version 6.11

    Currently I have a model called Folder which doesn't have $fillable nor $guarded defined. I created a new RepositoryFolder class and defined some rules. Now, I can store a new Model with any attributes defined in rules, which ignores $fillable.

    Accordingly to the documentation I was expected a MassAssign Exception.

    opened by MarcelBinarCode 1
  • Add setTitle method to base Filter class

    Add setTitle method to base Filter class

    Useful when doing

    public static function matches(): array
    {
        return [
            'invoice_id' => MatchFilter::make()
                ->setType('array')
                ->setTitle('Job Number')
                ->setRelatedRepositoryKey(InvoiceRepository::uriKey()),
        ];
    }
    
    opened by arthurkirkosa 1
  • Boolean Match not triggered when falsy value is sent

    Boolean Match not triggered when falsy value is sent

    Fixes https://github.com/BinarCode/laravel-restify/issues/453

    The problem was with the value for the query param... show_inactive=0 would result in (bool) 0 => false; altho show_inactive param was in query

    opened by arthurkirkosa 1
  • Issue action with not an actual database column

    Issue action with not an actual database column

    Hello,

    i have created a media relation

    `<?php

    namespace App\Restify\Actions\User;

    use App\Models\User; use Binaryk\LaravelRestify\Actions\Action; use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;

    class AttachUserAvatarRestifyAction extends Action { public function handle(RestifyRequest $request, User $user) { if ($request->hasFile('avatar')) {

            if ($user->avatar) {
                $user->avatar->delete();
            }
            
            $user->addMediaFromRequest('avatar')->toMediaCollection('avatars');
    
            return $user->avatar?->getUrl();
        }
    }
    

    } `

    and on UserRepository field('avatar', fn () => $this->model()->avatar?->getUrl())->rules(['image'])->action(new AttachUserAvatarRestifyAction)

    So all is good but i think the action trigger after the value of this field returns the old data returned.

    Thanks

    opened by hamdallah90 0
  • Docs for media is missing a function call to store the media

    Docs for media is missing a function call to store the media

    Existing

    https://github.com/BinarCode/laravel-restify/blob/f87b81b40f357608f9af2f7ee5a76dcf240b62d6/docs-v2/content/en/api/fields.md#L308

    Issue

    According to the docs for the media library, you need to use one of the "finishing methods" to store the media. Just calling addMediaFromRequest doesn't store the file.

    Suggested

            $post->addMediaFromRequest('file')
                ->toMediaCollection();
    
    opened by mahmoudawadeen 0
  • Relationships values should always be an array

    Relationships values should always be an array

    Schema

    User has many Post Post has many Note Note belong to User

    Policy

    Allow show for note when the note belongs to user.

    class NotePolicy
    {
        public function show(User $user, Note $note)
        {
           return $note->user_id == $user->id;
        }
    }
    

    Database

    Post | id | |---| | 1 |

    User | id | |---| | 1 | | 2 |

    Note | id | user_id | |---|---| | 1 | 2 | | 2 | 1 |

    Request

    GET /posts?related=note Whenever a user fetches a post it would load the notes but only the ones belonging to the user because of the NotePolicy.

    Response

    Actual

    {
       .....
       "relationships":{
          "notes":{
             "1":{
                "id":"2",
                "type":"notes"
             }
          }
       }
       .....
    }
    

    Expected

    {
       .....
       "relationships":{
          "notes":[
             {
                "id":"2",
                "type":"notes"
             }
          ]
       }
       .....
    }
    

    Root cause

    1. Resolve eager field uses allowToShow to nullify notes where the user is not allowed to access https://github.com/BinarCode/laravel-restify/blob/7.x/src/Fields/EagerField.php#L69
    2. Resolve relationship uses filter on the collection to remove nulls https://github.com/BinarCode/laravel-restify/blob/7.x/src/Repositories/Repository.php#L530

    Post loaded from db

    {
       "id":1,
       "notes":[
          {
             "id":"1",
             "type":"notes"
          },
          {
             "id":"2",
             "type":"notes"
          }
       ]
    }
    

    Post notes are filtered via allowToShow (step 1)

    {
       "id":1,
       "notes":[
         null,
          {
             "id":"2",
             "type":"notes"
          }
       ]
    }
    

    Post notes after removing nulls via filter (step 2)

    {
       "id":1,
       "notes":{
          1 => {
             "id":"2",
             "type":"notes"
          }
       }
    }
    

    Suggested fix

    Replace https://github.com/BinarCode/laravel-restify/blob/18d120ae886c2300307321c015501a2bcaccf7f8/src/Repositories/Repository.php#L543 with

    return $items->filter()->values();
    
    opened by mwalid-trackbar 1
  • "Class \"Binaryk\\LaravelRestify\\Tests\\Fixtures\\User\\User\" not found

    https://github.com/BinarCode/laravel-restify/blob/d0cff087b7212d66fdfabce479bd3de9f9e40f7f/src/Http/Controllers/Auth/VerifyController.php#L16

    /** * @var User $user */
    $user = config('restify.auth.user_model')::query()->findOrFail($id); 
    
    opened by mahmoudawadeen 2
Releases(7.7.1)
  • 7.7.1(Nov 22, 2022)

  • 7.7.0(Sep 17, 2022)

    Sync related

    You can also sync your BelongsToMany field. Say you have to sync permissions to a role. You can do it like this:

    POST: api/restify/roles/1/sync/permissions
    

    Payload:

    {
      "permissions": [1, 2]
    }
    

    Under the hood this will call the sync method on the BelongsToMany relationship:

    // $role of the id 1
    
    $role->permissions()->sync($request->input('permissions'));
    

    Authorize sync

    You can define a policy method syncPermissions. The name should start with sync and suffix with the plural CamelCase name of the model's relationship name:

    // RolePolicy.php
    
    public function syncPermissions(User $authenticatedUser, Company $company, Collection $keys): bool
    { 
        // $keys are the primary keys of the related model (permissions in our case) Restify is trying to `sync`
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 7.6.3(Sep 16, 2022)

  • 7.6.2(Sep 15, 2022)

    Fixed

    • Belongs search with custom foreign key https://github.com/BinarCode/laravel-restify/pull/512

    Inherited https://github.com/BinarCode/laravel-restify/releases/tag/6.12.2

    Source code(tar.gz)
    Source code(zip)
  • 7.6.1(Sep 15, 2022)

  • 7.6.0(Sep 15, 2022)

    Added

    • The policyMeta method is now protected at the repository level (it could be used to override the policy information for the show and index methods.
    • Do not make RelatedDto as a singleton in a test environment (this causes issues when trying to make few relationship requests to the same repository in the same test)

    Fixed

    • Always public index method if no policy
    • Unit test that shows routes list command.
    • Fix Gate::check - show - not working

    Support

    • Added unit tests for the testing helpers (action, route and getters).
    Source code(tar.gz)
    Source code(zip)
  • 6.12.2(Sep 15, 2022)

  • 7.5.4(Aug 2, 2022)

  • 7.5.3(Aug 1, 2022)

  • 7.5.2(Jul 30, 2022)

  • 7.5.1(Jul 30, 2022)

  • 7.5.0(Jul 30, 2022)

    Added

    This will enable policies to be cached by adding the configuration:

      'cache' => [
          /*
          | Specify the cache configuration for the resources policies.
          | When enabled, methods from the policy will be cached for the active user.
          */
          'policies' => [
              'enabled' => true,
    
              /*
              | ttl in seconds
              */
              'ttl' => 5 * 60,
          ],
      ],
    
    Source code(tar.gz)
    Source code(zip)
  • 7.4.0(Jul 29, 2022)

    Added

    • Added support for nested relationships when you want to get the parent and children in the same query. Imagine a tweet thread, where you want to list all tweets with its parent tweet along with its immediate children tweets. This is now possible and will do not run into infinite loop issue.

    Fixed

    • Support to mock repositories using YourRepository::partialMock() for the index request, previously it ran into the mock state and didn't update the second resources in tests.

    Breaking

    • The $eagerState repository property is now private, and it is of type null|string because it holds the parent repository that renders it.
    Source code(tar.gz)
    Source code(zip)
  • 7.3.1(Jul 26, 2022)

  • 7.3.0(Jul 25, 2022)

  • 7.2.1(Jul 24, 2022)

  • 7.2.0(Jul 24, 2022)

  • 7.1.0(Jul 24, 2022)

  • 7.0.0(Jul 24, 2022)

    [7.0.0] 2022-07-24

    • [x] Adding support for custom ActionLogs (ie ActionLog::register("project marked active by user Auth::id()", $project->id))
    • [x] Ensure $with loads relationship in show requests
    • [x] Make sure any action isn't permitted unless the Model Policy exists
    • [x] Having a helper method that allow to return data using the repository from a custom controller PostRepository::withModels(Post::query()->take(5)->get())->include('user')->serializeForShow() - see seralizer()
    • [x] Ability to make an endpoint public using a policy method
    • [x] Load specific fields for nested relationships (ie: api/restify/company/include=users.posts[id, name].comments[title])
    • [x] Load nested for relationships with a nested level higher than 2 (so now you can load any nested level you need a.b.c.d)
    • [x] Shorter definition of Related fields HasMany::make('posts')
    • [x] Performance improvements

    see Change Log and Upgrading guideline

    Source code(tar.gz)
    Source code(zip)
  • 6.12.1(Jul 15, 2022)

  • 6.12.0(Jul 14, 2022)

  • 6.11.1(Jul 13, 2022)

  • 6.11.0(May 19, 2022)

    Added

    • setTitle for filters so the FE could display them
    public static function matches(): array
    {
        return [
            'invoice_id' => MatchFilter::make()
                ->setType('array')
                ->setTitle('Job Number')
                ->setRelatedRepositoryKey(InvoiceRepository::uriKey()),
        ];
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 6.10.0(May 4, 2022)

    Added

    Bulk delete flow

    The payload for a bulk delete should contain an array of primary keys for the models you want to delete:

    [
      1, 10, 15
    ]
    

    These models will be resolved from the database and check for the deleteBulk policy permission, in case any of the models isn't allowed to be deleted, no entry will be deleted.

    Source code(tar.gz)
    Source code(zip)
  • 6.9.3(Apr 14, 2022)

  • 6.9.2(Mar 29, 2022)

  • 6.9.1(Mar 4, 2022)

  • 6.9.0(Mar 4, 2022)

  • 6.8.2(Feb 23, 2022)

  • 6.8.1(Feb 4, 2022)

Owner
BinarCode
BinarCode is a software development company with focus on web development and open source
BinarCode
JSONFinder - a library that can find json values in a mixed text or html documents, can filter and search the json tree, and converts php objects to json without 'ext-json' extension.

JSONFinder - a library that can find json values in a mixed text or html documents, can filter and search the json tree, and converts php objects to json without 'ext-json' extension.

Eboubaker Eboubaker 2 Jul 31, 2022
Which is the fastest web framework?

Which is the fastest ? Simple framework comparison Motivation There are many frameworks, each one comes with its own advantages and drawbacks. The pur

The Benchmarker 6.6k Jan 2, 2023
Laravel Blog Package. Easiest way to add a blog to your Laravel website. A package which adds wordpress functionality to your website and is compatible with laravel 8.

Laravel Blog Have you worked with Wordpress? Developers call this package wordpress-like laravel blog. Give our package a Star to support us ⭐ ?? Inst

Binshops 279 Dec 28, 2022
A plugin to make Nextcloud compatible with Solid

solid-nextcloud A plugin to make Nextcloud compatible with Solid.

PDS Interop 54 Jan 2, 2023
PDF API. JSON to PDF. PDF Template Management, Visual HTML Template Editor and API to render PDFS by json data

PDF Template Management, Visual HTML Template Editor and API to render PDFS by json data PDF ENGINE VERSION: development: This is a prerelease version

Ajous Solutions 2 Dec 30, 2022
Allows generate class files parse from json and map json to php object, including multi-level and complex objects;

nixihz/php-object Allows generate class files parse from json and map json to php object, including multi-level and complex objects; Installation You

zhixin 2 Sep 9, 2022
Json-normalizer: Provides generic and vendor-specific normalizers for normalizing JSON documents

json-normalizer Provides generic and vendor-specific normalizers for normalizing JSON documents. Installation Run $ composer require ergebnis/json-nor

null 64 Dec 31, 2022
The easiest way to match data structures like JSON/PlainText/XML against readable patterns. Sandbox:

PHP Matcher Library created for testing all kinds of JSON/XML/TXT/Scalar values against patterns. API: PHPMatcher::match($value = '{"foo": "bar"}', $p

Coduo 774 Dec 31, 2022
A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package.

Net A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package. Features: No hard dependencies; Favours

Minibase 16 Jun 7, 2022
A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package.

Net A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package. Features: No hard dependencies; Favours

Minibase 16 Jun 7, 2022
A simple social groups compatible with ActivityPub.

A simple social groups compatible with ActivityPub.

wxw.moe 23 Dec 2, 2022
Algolia Search integration for Magento 1 - compatible with versions from 1.6.x to 1.9.x

Algolia Search for Magento 1.6+ End of Support ?? The Algolia Magento 1 extension has reached End of Life regarding support and maintenance. We do not

Algolia 163 Dec 20, 2022
[ONLY Magento 2.0.x Compatible] Code samples for Magento developers

Synopsis This project is a collection of samples to demonstrate technologies introduced in Magento 2. You will find the most simple extension along wi

Magento 58 Dec 26, 2022
Algolia Search integration for Magento 2 - compatible with versions from 2.3.x to 2.4.x

Algolia Search for Magento 2 ?? Need help? Check out our Technical Troubleshooting Guide. For feedback, bug reporting, or unresolved issues with the e

Algolia 147 Dec 20, 2022
Faker-driven, configuration-based, platform-agnostic, locale-compatible data faker tool

Masquerade Faker-driven, platform-agnostic, locale-compatible data faker tool Point Masquerade to a database, give it a rule-set defined in YAML and M

elgentos ecommerce solutions 219 Dec 13, 2022
TiDB is an open source distributed HTAP database compatible with the MySQL protocol

What is TiDB? TiDB ("Ti" stands for Titanium) is an open-source NewSQL database that supports Hybrid Transactional and Analytical Processing (HTAP) wo

PingCAP 33.1k Jan 9, 2023
Pika is a nosql compatible with redis, it is developed by Qihoo's DBA and infrastructure team

Introduction中文 Pika is a persistent huge storage service , compatible with the vast majority of redis interfaces (details), including string, hash, li

OpenAtomFoundation 4.9k Jan 6, 2023
Allows installing Drupal extensions event if not compatible with installed drupal/core package

mglaman/composer-drupal-lenient Lenient with it, Drupal 10 with it. Why? The Drupal community introduced a lenient Composer facade that modified the d

Matt Glaman 14 Dec 18, 2022
Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests

PHPUnit Polyfills Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests. Requirements Instal

Yoast 147 Dec 12, 2022