πŸžπŸ§‘β€πŸ³ An on-the-fly GraphQL Schema generator from Eloquent models for Laravel.

Overview

Laravel Bakery logo

Build Status status Latest Version Code Quality Total Downloads License


An on-the-fly GraphQL Schema generator from Eloquent models for Laravel.

Installation

This package requires PHP 7.2 and Laravel 6 or higher. To get started with Bakery, simply run:

composer require scrnhq/laravel-bakery

Quickstart

After installing Bakery, publish the configuration and asserts using the bakery:install Artisan command.

php artisan bakery:install

After running this command, the configuration file should be located at config/bakery.php. The default App\Bakery\User Bakery model schema refers to the App\User model.

You can find your new GraphQL API at /graphql and you can navigate to /graphql/explore to find GraphiQL, the graphical interactive GraphQL IDE.

query {
  users {
    items {
      id
    }
  }
}

Model schemas

Model schemas are classes that lets you connect your Eloquent models with the GraphQL API. In there you can define which fields are available, which of them can be mutated and much more.

By default, Bakery model schema's are stored in the app\Bakery directory. You can generate a new model schema using the handy bakery:modelschema Artisan command.

php artisan bakery:modelschema Post

The model property of a model schema defines which Eloquent model it corresponds to.

/**
 * The model the schema corresponds to.
 *
 * @var string
 */
protected $model = \App\Post::class;

Registering model schemas

All model schema's in the app/Bakery directory will automatically be registered by Bakery. If you choose to store your model schema's differently, you need to define and register your schema manually.

You are not required to manually define and register a Schema. You can skip this step if you do not wish to manually register a schema.

In order to make model schemas available within GraphQL, they must be registered in a Schema. First you must create a new Schema class. Next, you should set the schema item in the config/bakery.php file to the newly created Schema.

There are two ways to manually registering model schemas in Bakery. You can use the modelsIn method in the schema to load all models schemas in a given directory, or you can manually return an array of models schemas.

namespace App\Support;

use Bakery\Support\Schema as BaseSchema;

class Schema extends BaseSchema
{
    /*
     * Get the models for the schema.
     *
     * @return array
     */
    public function models()
    {
        return $this->modelsIn(app_path('Bakery'));
        
        // Or, manually.
        return [
            App\Bakery\User::class,
            App\Bakery\Post::class,
        ];
    }
}

Now that you have created and registered your model schemas with Bakery, you can browse to /graphql/explore and query your models in the interactive playground GraphQL.

query {
  posts {
    items {
      id
    }
  }
}

If everything is set up properly you will get a collection of posts in your database. You can also use GraphQL to retrieve a single post.

query {
  posts(id: "1") {
    id
  }
}

Just like Laravel, Bakery follows naming conventions. It uses Laravel's pluralization library to transform your model into queries so you can fetch an individual Post with post and a collection of Posts with posts.

Fields

Now, each Bakery model schema contains a fields that return an array of fields, which extend the \Bakery\Fields\Field class. To add a field to model schema, simply add it to fields method, where the key of the item must match the name of the model attribute.

use Bakery\Field;

/**
 * Get the fields for the schema.
 *
 * @return array
 */
public function fields(): array
{
    return [
        'title' => Field::string(),
    ];
}

Now you can query the title of the posts in GraphQL.

query {
  post(id: "1") {
    id
    title
  }
}

Field Types

Bakery has the following fields available:

Boolean
Field::boolean()
Float
Field::float()
ID
Field::ID()
Int
Field::int()
String
Field::string()

Relations

In addition to the fields described above, Bakery supports Eloquent relationships, too. To add a relationship to the model schema, simply add it to the relations method, where the key of the item must match the relation name. Let's say a User model hasMany Post models. Then you would define your Bakery model schema's like so:

app\Bakery\User.php

use Bakery\Field;
use App\Bakery\Post;

/**
 * Get the fields for the schema.
 *
 * @return array
 */
public function relations()
{
    return [
        'posts' => Field::collection(Post::class),
    ];
}

The inverse of the previous relation is that a Post model belongsTo a User model. The Bakery model schema would be defined like so:

app\Bakery\Post.php

use Bakery\Field;
use App\Bakery\User;

/**
 * Get the fields for the schema.
 *
 * @return array
 */
public function relations()
{
    return [
        'user' => Field::model(User::class),
    ];
}

This way you can get all posts related to a user within a single GraphQL query.

query {
  user(id: "1") {
    id
    posts {
      id
    }
  }
}

Mutations

Another key feature of GraphQL that Bakery fully supports are mutations. Bakery automatically creates the create, update, and delete mutations for each registered model. Bakery also seamlessly uses Laravel's policies to authorize the actions of your users.

Having policies for your models is required for Bakery mutations to work. See https://laravel.com/docs/5.7/authorization for more information.

For example, with the model schemas mentioned above, you could create a Post with a simple GraphQL mutation.

mutation {
  createPost(input: {
    title: "Hello world!"
  }) {
    id
  }
}
Comments
  • πŸ“ˆOrder by relationship

    πŸ“ˆOrder by relationship

    This is a first draft of trying to order by relationship. This definitely needs some more tests as we need to join the relationship to be able to sort on it. So this might not work for every relation type.

    This is also a breaking change because the API for ordering will change. A welcome side effect is that it allows us to filter on multiple columns and relations.

    @erikgaal what do you think?

    review needed :memo: 
    opened by rovansteen 6
  • Mutation are not working

    Mutation are not working

    Describe the bug

    Mutation are not working

    To Reproduce

    Define Model

    <?php
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    use GraphQL\Type\Definition\Type;
    use Bakery\Eloquent\Introspectable;
    use Bakery\Eloquent\Mutable;
    
    class Group extends Model
    {
    
        use Mutable;
        use Introspectable;
    
        protected $fillable = [
            'name',
        ];
    
        /**
         * Declare GraphQL fields
         */
        public function fields() {
            return [
                'id' => Type::id(),
                'name' => Type::string(),
            ];
        }
    }
    

    Do query: OK

    query allGroups {
      groups {
        items {
          id
        	name
        }
      }
    }
    

    Do mutation: Failed!

    Error App\Models\Group has no registered model schema in Bakery.

    mutation createGroup {
      
      createGroup(input: {
       	name: "Group Created by GraphQL",
      }) {
        id
      }
      
    }
    

    I have try to debug as hard as I can.

    I think something broken with

    Bakery::addModelSchemas($this->getModels());
    // or
    addModelSchema();
    
    • Laravel Version: 5.6.31
    • Bakery Version: 1.0.7
    opened by olragon 3
  • Remove user parameter from getGate method

    Remove user parameter from getGate method

    Due to a bug in a new feature in Laravel 5.8, policies aren't loaded when using the policy name guesser and Gate::forUser https://github.com/laravel/framework/pull/27708.

    Since this call isn't really required, as the authenticated user is the same as the passed one here, we can remove the call to simply fix this issue right now.

    opened by erikgaal 2
  • πŸ—„ Model schemas

    πŸ—„ Model schemas

    This PR aims to finalise the API for Bakery 2.0 regarding the schema for models.

    • Enforce creating separate classes for model schemas (rather than extending behaviour of Eloquent models)
    • Deprecating the need for putting traits on the models used by the model schemas
    • Deprecate the authorizedForReading
    • Deprecate relying on model's fillable fields for defining if a field is fillable or not.

    This paves the way for scoping queries on the model schemas and defining if a model can be indexed or mutated.

    Some things left to do:

    • [x] Bring back the persisting & persisted events
    • [x] Make the getting/resolving model schemas API a little bit more clear
    • [ ] ~Make the schema serialisable~
    in progress :construction: 
    opened by rovansteen 2
  • Revamp policies

    Revamp policies

    Right now the way Bakery implements policies for nested mutations is that they are always called on the parent. For example, if you execute a mutation like this:

    mutation {
      createArticle(input: {title: "Blog post", user: {name: "John Doe"}}) {
        id
      }
    }
    

    This creates an article with an associated user. Right now it would call the following policies:

    • ArticlePolicy@create
    • ArticlePolicy@createUser

    This is weird for two reasons: first we don't call the UserPolicy@create so if there's some general policy on if you can create a user or not you have to duplicate that or call the create method on the UserPolicy from the createUser method on the ArticlePolicy.

    Second semantically it feels weird to define it like this. Because the user is a belongsTo relationship on the article (an article belongs to a user) it feels more natural to say, can I create an article for this user? Thus it would make more sense to call UserPolicy@createArticle. The issue with the create{relationship} is that it is already used by Laravel Nova to check if the user has the possibility to create the relationship on the model, not if it is allowed in that particular case.

    A different naming convention we could use use is the methods that Laravel exposes to help you set up these relationship. In the example above that would be the associate method. So then we could call UserPolicy@associateArticle and pass in the article to check if this specific association is allowed.

    Let's break this down for every relationship type

    One to One

    Here we will call the policy of the hasOne part of the relationship with the belongsTo part. E.g. a user has one phone, a phone belongs to a user. We call the PhonePolicy@create and UserPolicy@savePhone.

    One to Many

    Here we will call the policy of the hasMany part of the relationship with the belongsTo part. E.g. a user has many articles, an article belongs to a user. We call the ArticlePolicy@create and UserPolicy@addArticle.

    Many to Many

    Many to many is a bit trickier because both sides are 'equal' here. So there we use the direction you are executing to determine which side we are checking. If you have an article that has many tags, and you create an article with tags, we will call ArticlePolicy@create and ArticlePolicy@attachTag. Note that this is exactly the same way Laravel Nova does it with the same parameters, so this should work fine together.

    What do you think @erikgaal?

    discussion :speaking_head: 
    opened by rovansteen 2
  • πŸš” Store policies

    πŸš” Store policies

    This PR aims to make policies for storing data more clear. It tries to following a Nova-like API to make it easier to use in the Laravel ecosystem.

    • [x] Remove magic set{$field}Attribute policy.
    • [x] No longer use context as a way to store the currently authenticated user, but rather use auth()->user() consistent.
    • [x] Add $field->storePolicy($policy) method.
    • [x] Add $field->canStore(Closure $closure) method.
    • [x] Add $field->canStoreWhen(string $policy) method.
    opened by rovansteen 2
  • πŸ”₯ Hotfix β€” Fix issue with model schemas

    πŸ”₯ Hotfix β€” Fix issue with model schemas

    This fixes the issue described in #55, where mutations are not working because the model schema is only registered when it's not an eloquent model itself.

    hotfix :fire: 
    opened by rovansteen 2
  • Schema not found error after installation

    Schema not found error after installation

    Describe the bug A clear and concise description of what the bug is.

    To Reproduce Steps to reproduce the behavior:

    • Laravel Version: [e.g. 7.6.1]
    • Bakery Version [3.3.3]

    Hi ,

    I got an error when running the pluggin, when i rud the endpoint/graphql/explore

    "message": "Class 'App\Bakery\Support\Schema' not found", "exception": "Error",

    opened by anoop151 1
  • Pivot fields missing for uni-directional relation

    Pivot fields missing for uni-directional relation

    Describe the bug When a relation is only specified in one direction on the ModelSchema, then the pivot fields are on the wrong side of the relation. For example, when there is a relationship users belongs to many roles, there is a rolesPivot on the User type. While there should only be a usersPivot on the Roles type, because there is no way to retrieve the users from the roles.

    To Reproduce

    // UserSchema.php
    public function relations(): array
    {
        return [
            'roles' => Field::collection(RoleSchema::class),
        ];
    }
    
    // RoleSchema.php
    public function relations(): array
    {
        return [];
    }
    
    // UserRole.php
    public function fields(): array
    {
        return [
            'comment' => Field::string(),
        ];
    }
    
    {
      user(id: 1) {
        roles {
          userPivot { comment }
        }
      }
    }
    
    # "{"errors":[{"message":"Cannot query field \"userPivot\" on type \"Role\".","category":"graphql","locations":[{"line":3,"column":17}]}]}"
    
    • Laravel Version: v5.7.27
    • Bakery Version v3.0.0
    bug :bug: 
    opened by erikgaal 0
  • SoftDeletes support

    SoftDeletes support

    Is your feature request related to a problem? Please describe. There is currently no way to retrieve all models including the soft-deleted models according to the SoftDeletes trait.

    Describe the solution you'd like withTrashed: Boolean argument on Collection to include soft-deleted models. There should also be a way to authorize or scope who can view soft-deleted models.

    enhancement :sparkles: 
    opened by erikgaal 0
Releases(v3.3.11)
Owner
Scrn
Scrn
Monorepo of the PoP project, including: a server-side component model in PHP, a GraphQL server, a GraphQL API plugin for WordPress, and a website builder

PoP PoP is a monorepo containing several projects. The GraphQL API for WordPress plugin GraphQL API for WordPress is a forward-looking and powerful Gr

Leonardo Losoviz 265 Jan 7, 2023
Syntax to query GraphQL through URL params, which grants a GraphQL API the capability to be cached on the server.

Field Query Syntax to query GraphQL through URL params, which grants a GraphQL API the capability to be cached on the server. Install Via Composer com

PoP 4 Jan 7, 2022
Monorepo of the PoP project, including: a server-side component model in PHP, a GraphQL server, a GraphQL API plugin for WordPress, and a website builder

PoP PoP is a monorepo containing several projects. The GraphQL API for WordPress plugin GraphQL API for WordPress is a forward-looking and powerful Gr

Leonardo Losoviz 265 Jan 7, 2023
PHP implementation of JSON schema. Fork of the http://jsonschemaphpv.sourceforge.net/ project

JSON Schema for PHP A PHP Implementation for validating JSON Structures against a given Schema with support for Schemas of Draft-3 or Draft-4. Feature

Justin Rainbow 3.4k Dec 26, 2022
Code shared by the schema packages

Code shared by the schema packages

PoP CMS Schema 2 Nov 4, 2022
A robust JSON decoder/encoder with support for schema validation.

A robust wrapper for json_encode()/json_decode() that normalizes their behavior across PHP versions, throws meaningful exceptions and supports schema validation by default.

Bernhard Schussek 356 Dec 21, 2022
Like FormRequests, but for validating against a json-schema

JSON Schema Request Laravels Form Request Validation for JSON Schema documents Installation composer require wt-health/laravel-json-schema-request Us

Webtools Health 1 Feb 3, 2022
Laravel wrapper for Facebook's GraphQL

Laravel GraphQL Use Facebook's GraphQL with Laravel 6.0+. It is based on the PHP port of GraphQL reference implementation. You can find more informati

Mikk Mihkel Nurges 1.9k Dec 31, 2022
A framework for serving GraphQL from Laravel

Lighthouse A framework for serving GraphQL from Laravel Lighthouse is a GraphQL framework that integrates with your Laravel application. It takes the

NuWave Commerce 3.1k Jan 6, 2023
GraphQL implementation with power of Laravel

Laravel GraphQL Use Facebook GraphQL with Laravel 5.2 >=. It is based on the PHP implementation here. You can find more information about GraphQL in t

Studionet 56 Mar 9, 2022
Create REST and GraphQL APIs, scaffold Jamstack webapps, stream changes in real-time.

API Platform is a next-generation web framework designed to easily create API-first projects without compromising extensibility and flexibility: Desig

API Platform 7.7k Jan 7, 2023
This bundle provides tools to build a complete GraphQL server in your Symfony App.

OverblogGraphQLBundle This Symfony bundle provides integration of GraphQL using webonyx/graphql-php and GraphQL Relay. It also supports: batching with

Webedia - Overblog 720 Dec 25, 2022
Pure PHP implementation of GraphQL Server – Symfony Bundle

Symfony GraphQl Bundle This is a bundle based on the pure PHP GraphQL Server implementation This bundle provides you with: Full compatibility with the

null 283 Dec 15, 2022
GraphQL Bundle for Symfony 2.

Symfony 2 GraphQl Bundle Use Facebook GraphQL with Symfony 2. This library port laravel-graphql. It is based on the PHP implementation here. Installat

Sergey Varibrus 35 Nov 17, 2022
Test your PHP GraphQL server in style, with Pest!

Pest GraphQL Plugin Test your GraphQL API in style, with Pest! Installation Simply install through Composer! composer require --dev miniaturebase/pest

Minibase 14 Aug 9, 2022
Add Price Including tax for Magento's "cart" GraphQl query

Comwrap_GraphQlCartPrices Add Price Including tax for Magento's "cart" GraphQl query Query will looks like following: items { id __typenam

Comwrap 1 Dec 2, 2021
GraPHPinator ⚑ 🌐 ⚑ Easy-to-use & Fast GraphQL server implementation for PHP

Easy-to-use & Fast GraphQL server implementation for modern PHP. Includes features from latest draft, middleware directives and modules with extra functionality.

Infinityloop.dev 34 Dec 14, 2022
A Statamic Pro addon that provides alternative GraphQL queries for collections, entries and global sets.

Statamic Enhanced GraphQL A Statamic CMS GraphQL Addon that provides alternative GraphQL queries for collections, entries and global sets. ⚠️ This is

Grischa Erbe 2 Dec 7, 2021
Place where I record all knowledge gained for GraphQL from Laracasts & other tutorials.

Knowledge from Laracasts series: https://laracasts.com/series/graphql-with-laravel-and-vue What is GraphQL It is a query language for your API, and it

Nikola 0 Dec 26, 2021