Skip to content

Commit

Permalink
⚙️ Configuration and install enhancements (#80)
Browse files Browse the repository at this point in the history
* Clean up config file and service provider

Resolves #65

* Implement install and generator commands

* Automatically discover model schemas in App\Bakery directory

Resolves #79

* Resolve schema as non mutable if there are no fillable fields

* Add IntegrationTestSchema and fix the tests

* Apply fixes from StyleCI

* Add Schema config value

* Update the README

* Add tests for schema automatic discovery

* Update README
  • Loading branch information
erikgaal authored Nov 15, 2018
1 parent b0c0638 commit b5e5e5d
Show file tree
Hide file tree
Showing 36 changed files with 740 additions and 332 deletions.
268 changes: 173 additions & 95 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ An on-the-fly GraphQL Schema generator from Eloquent models for Laravel.
- [Version Compatibility](#version-compatibility)
- [Installation](#installation)
- [Quickstart](#quickstart)
- [Model schemas](#model-schemas)

## Version Compatibility

Expand All @@ -28,7 +29,7 @@ Install via composer:
composer require scrnhq/laravel-bakery
```

or require in _composer.json_:
or require in `composer.json`:

```json
{
Expand All @@ -40,185 +41,262 @@ or require in _composer.json_:

then run `composer update` in your terminal to install Bakery.

Once this has finished, you will need to add the service provider to the providers array in your `app.php` config as follows:
## Quickstart

**This package supports Laravel's package auto-discovery; if you are using Laravel 5.5 or above you can skip this step.**
After installing Bakery, publish the configuration and asserts using the `bakery:install` Artisan command.

```php
Bakery\BakeryServiceProvider::class,
```
php artisan bakery:install
```

## Quickstart
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.

First publish the configuration file of Bakery by running the following command in your terminal:
You can find your new GraphQL API at `/graphql` and you can navigate to `/graphql/explore` to find GraphiQL, the
graphical interactive GraphQL IDE.

```gql
query {
users {
items {
id
}
}
}
```
php artisan vendor:publish --provider="Bakery\BakeryServiceProvider"

## Model schemas

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.

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

Now open up `config/bakery.php` and you will see a `models` property that contains an empty array.
Here we can start defining the model schemas for our models. Model schemas are classes that lets you define fields,
relationships and behaviour for your models that Bakery uses to set up a GraphQL schema.
### 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.**

Let's start by creating a model schema! You can put these wherever you want, for example you can put them in a
`ModelSchemas` directory.
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.

```php
namespace App\ModelSchemas\UserSchema;
namespace App\Support;

use App\User;
use Bakery\Eloquent\ModelSchema;
use Bakery\Support\Facades\Bakery;
use Bakery\Support\Schema as BaseSchema;

class UserSchema extends ModelSchema
class Schema extends BaseSchema
{
protected $model = User::class;

public function fields() {
return [
'email' => Bakery::string(),
];
}
/*
* 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,
];
}
}
```

### Queries

To test this out, open up your Laravel application and go to `/graphiql`. Here you will see an interactive playground to execute GraphQL queries and mutations. Now execute the following query (assuming you have made your User model introspectable):
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.

```gql
query {
users {
posts {
items {
id
}
}
}
```

If everything is set up properly you will get a collection of users in your database! Now to fetch an individual user you can execute the following query:
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.

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

Just like Laravel, Bakery follows certain naming conventions. It uses Laravel's pluralization library to transform your model name in to queries so you can fetch an individual user by _user_ and a collection of users by _users_.
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

One of the differences between GraphQL and Eloquent is that GraphQL is a little bit stricter when it comes to defining its schemas than Laravel is to defining its models. To create the types and queries you need to tell us a little bit about which attributes on your model you want to expose! These attributes are called `fields` in GraphQL and you can define them by that name on your model like so:

```diff
<?php

namespace App;
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 GraphQL\Type\Definition\Type;
use Bakery\Eloquent\Introspectable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
```php
use Bakery\Field;

/**
* Get the fields for the schema.
*
* @return array
*/
public function fields(): array
{

+ public function fields()
+ {
+ return [
+ 'email' => Type::string();
+ ];
+ }

return [
'title' => Field::string(),
];
}
```

This tells Bakery that there is an email field on the model with the type string. [Here you can see a full overview and documentation of the underlying GraphQL type system](http://webonyx.github.io/graphql-php/type-system/).

Now you are able to query email addresses from your query, like so:
Now you can query the title of the posts in GraphQL.

```gql
query {
user(id: "1") {
post(id: "1") {
id
email
title
}
}
```

### Relations
#### Field Types

Bakery is also capable of returning data of other models related to the model you are querying. Let's say a user has articles, so you have defined a relationship on the model like so:
Bakery has the following fields available:

`User.php`
- [Boolean](#boolean)
- [Float](#float)
- [ID](#id)
- [Int](#int)
- [String](#string)

##### Boolean
```php
public function articles()
{
return $this->hasMany(Article::class);
}
Field::boolean()
```

`Article.php`

##### Float
```php
<?php

namespace App;
Field::float()
```

use GraphQL\Type\Definition\Type;
use Bakery\Eloquent\Introspectable;
use Illuminate\Database\Eloquent\Model;
##### ID
```php
Field::ID()
```

class Article extends Model
{
##### Int
```php
Field::int()
```

public function fields()
{
return [
'title' => Type::string(),
];
}
}
##### String
```php
Field::string()
```

Now we need to tell Bakery about this new article model by updating the config.
### 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:

`config/bakery.php`
`app\Bakery\User.php`

```diff
return [
'models' => [
App\User::class,
+ App\Article::class,
],
];
```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),
];
}
```

Once you have set up your relationship in Eloquent, you just need to define it so that Bakery knows about it by setting the relations field on your user model:
The inverse of the previous relation is that a `Post` model `belongsTo` a `User` model. The Bakery model schema
would be defined like so:

`User.php`
`app\Bakery\Post.php`

```php
use Bakery\Field;
use App\Bakery\User;

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

Now you can easily fetch all the articles from a user in a single query like so:
This way you can get all posts related to a user within a single GraphQL query.

```gql
query {
user(id: "1") {
id
articles {
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.6/authorization for more information.
For example, with the model schemas mentioned above, you could create a `Post` with a simple GraphQL mutation.

```gql
mutation {
createPost(input: {
title: "Hello world!"
}) {
id
}
}
```
Loading

0 comments on commit b5e5e5d

Please sign in to comment.