A composer package designed to help you create a JSON:API in Phalcon

Overview

phalcon-json-api-package

A composer package designed to help you create a JSON:API in Phalcon

What happens when a PHP developer wants to create an API to drive their client-side SPA? Well you start with Phalcon (A modern super fast framework) loosely follow the JSON:API and package it up for Composer. The result is the phalcon-json-api package (herafter referred to as the API) so enjoy.

System Requirements

Release Notes

Read up on the latest plans for the API here.

How is Phalcon used?

Phalcon is the underlying framework this project depends on. Any user of the API package will need to have a working installation of Phalcon already installed on their system. The API makes extensive use of Phalcon sub systems including the ORM, Router and Service Locator.

How is JSON:API used?

The Phalcon JSON API package attempts to follow the JSON API as closely as possible. There are several enhancements this project incorporates beyond the JSON API specification.

How can I quickly see this project in action?

New folks are encouraged to download and install the sister project that acts as a simple example application to demonstrate how one could use the API. This simple application include all the building blocks that make up the api including use of traditional Phalcon objects like Controllers and Models along with objects designed for use in the API such as Entities, Route and SearchHelpers.

How can I install this project?

Aside from meeting the system requirements, you should include this project in your composer file. Here is an example composer file that includes a few extra libraries needed for testing and timing api responses.

{
    "require": {
        "jsanc623/phpbenchtime": "dev-master",
        "gte451f/phalcon-json-api-package": "dev-master"
    },
    "require-dev": {
        "codeception/codeception": "*",
        "flow/jsonpath": "dev-master"
    }
}

Where is the wiki?

Lots more help is available here.

Where do babies come from?

The Stork Silly!

Comments
  • feat - make fetching model for a controller more declarative

    feat - make fetching model for a controller more declarative

    Improves loading of models to use the native getModel() hook for controllers.

    Takes a more declarative approach and allows for more flexible model directory organisation in consuming applications.

    opened by lougreenwood 1
  • Support for nested relationship?

    Support for nested relationship?

    Does the library support nested relationship?

    http://jsonapi.org/format/#fetching-relationships

    In order to request resources related to other resources, a dot-separated path for each relationship name can be specified:

    GET /articles/1?include=comments.author HTTP/1.1
    Accept: application/vnd.api+json
    
    opened by kumy 1
  • TM-785 - allow run unit tests without argv

    TM-785 - allow run unit tests without argv

    and allow set a profile on authenticator, this changes are needed in order the branch https://bitbucket.org/s_c/tm2-api/pull-requests/171/tm-785/diff works correctly.

    opened by marceloandrader 1
  • Moving throwOnSave behavior from Entity to BaseModel

    Moving throwOnSave behavior from Entity to BaseModel

    This makes all models throw exceptions when their save() calls fail.

    However, while I feel this throwing behavior should be on by default, that's also a breaking change. The behavior is easily overridden by adding a config line, but that doesn't make it "less breaking change":

    BaseModel::$throwOnSave = false; //this can be set on config/services.php, or anywhere in controllers
    $myModel->throwOnNextSave = false; //used only once, valid for the next save() call
    $myModel->throwOnNextSave(false)->save(); //same as above, easier to read
    

    We could:

    1. release it defaulting to true as 2.1 and breaking SemVer a bit;
    2. release as 3.0 and just pretend 2.0 never happened but keeping SemVer valid;
    3. leave it as false for BC, and use a config line to enable that on a per-project basis.

    How do you feel about that feature, should it be on or off by default?

    opened by igorsantos07 1
  • Empty

    Empty "include" and no relation alias defined raise an error

    If a model has relations without alias like:

    class News extends \PhalconRest\API\BaseModel {
    [...]
    public function initialize() {
            parent::initialize();
            $this->belongsTo('user_id', Users::class, 'id');
            $this->hasMany('id', NewsComments::class, 'news_id');
        }
    }
    

    A request to http://192.168.125.64/v1/news?include= (notice the empty "include") Raise an error:

    Notice: ob_clean(): failed to delete buffer. No buffer to delete in /var/www/vendor/gte451f/phalcon-json-api-package/src/bin/errorHandler.php on line 162
    {
      "errors": [
        {
          "title": "Fatal Error Ocurred - NOTICE",
          "code": 8,
          "detail": "Undefined property: Phalcon\\Mvc\\Model\\Row::$Users",
          "meta": {
            "file": "\/var\/www\/vendor\/gte451f\/phalcon-json-api-package\/src\/API\/Entity.php",
            "line": 497,
            "stack": [
              {
                "file": "\/var\/www\/vendor\/gte451f\/phalcon-json-api-package\/src\/API\/Entity.php",
                "line": 497,
                "function": "{closure}"
              },
              {
                "file": "\/var\/www\/vendor\/gte451f\/phalcon-json-api-package\/src\/API\/Entity.php",
                "line": 442,
                "function": "processStandardRelationships",
                "class": "PhalconRest\\API\\Entity",
                "object": {
                  "activeRelations": {
                    "Users": {
                      
                    }
                  },
                  "result": {
                    "outputMode": "error",
                    "di": {
                      
                    }
                  },
                  "primaryKeyValue": "1",
                  "searchHelper": {
                    "suppliedLimit": null,
                    "suppliedOffset": null,
                    "suppliedWith": "",
                    "suppliedSort": null,
                    "entityLimit": 500,
                    "entityOffset": null,
                    "entityWith": "none",
                    "entitySort": null,
                    "entitySearchFields": [
                      
                    ],
                    "isPartial": false,
                    "isPager": true,
                    "isCount": false,
                    "relationships": null,
                    "suppliedSearchFields": [
                      
                    ],
                    "di": {
                      
                    }
                  },
                  "hasManyToManyRegistry": [
                    
                  ],
                  "di": {
                    
                  }
                },
                "type": "->"
              },
              {
                "file": "\/var\/www\/vendor\/gte451f\/phalcon-json-api-package\/src\/API\/Entity.php",
                "line": 186,
                "function": "processRelationships",
                "class": "PhalconRest\\API\\Entity",
                "object": {
                  "activeRelations": {
                    "Users": {
                      
                    }
                  },
                  "result": {
                    "outputMode": "error",
                    "di": {
                      
                    }
                  },
                  "primaryKeyValue": "1",
                  "searchHelper": {
                    "suppliedLimit": null,
                    "suppliedOffset": null,
                    "suppliedWith": "",
                    "suppliedSort": null,
                    "entityLimit": 500,
                    "entityOffset": null,
                    "entityWith": "none",
                    "entitySort": null,
                    "entitySearchFields": [
                      
                    ],
                    "isPartial": false,
                    "isPager": true,
                    "isCount": false,
                    "relationships": null,
                    "suppliedSearchFields": [
                      
                    ],
                    "di": {
                      
                    }
                  },
                  "hasManyToManyRegistry": [
                    
                  ],
                  "di": {
                    
                  }
                },
                "type": "->"
              },
              {
                "file": "\/var\/www\/vendor\/gte451f\/phalcon-json-api-package\/src\/API\/BaseController.php",
                "line": 170,
                "function": "find",
                "class": "PhalconRest\\API\\Entity",
                "object": {
                  "activeRelations": {
                    "Users": {
                      
                    }
                  },
                  "result": {
                    "outputMode": "error",
                    "di": {
                      
                    }
                  },
                  "primaryKeyValue": "1",
                  "searchHelper": {
                    "suppliedLimit": null,
                    "suppliedOffset": null,
                    "suppliedWith": "",
                    "suppliedSort": null,
                    "entityLimit": 500,
                    "entityOffset": null,
                    "entityWith": "none",
                    "entitySort": null,
                    "entitySearchFields": [
                      
                    ],
                    "isPartial": false,
                    "isPager": true,
                    "isCount": false,
                    "relationships": null,
                    "suppliedSearchFields": [
                      
                    ],
                    "di": {
                      
                    }
                  },
                  "hasManyToManyRegistry": [
                    
                  ],
                  "di": {
                    
                  }
                },
                "type": "->"
              },
              {
                "function": "get",
                "class": "PhalconRest\\API\\BaseController",
                "object": {
                  "singularName": "News",
                  "pluralName": "News",
                  "di": {
                    
                  }
                },
                "type": "->"
              }
            ],
            "context": {
              "relation": {
                
              },
              "baseRecord": {
                "phalconRest\\Models\\News": {
                  "id": "1",
                  "date": "2007-10-26 18:31:20",
                  "czas_postu": "2007-10-26 20:31:20",
                  "title": "",
                  "content": "xx",
                  "username": "x",
                  "user_id": "1",
                  "comments_count": "0",
                  "last_comment": null
                },
                "phalconRest\\Models\\Users": {
                  "id": "1",
                  "username": "x",
                  "old_password": "",
                  "password": "",
                  "email": "xx",
                  "joined": "2007-10-26 19:46:44",
                  "daily_mails": "1",
                  "ip": "10.0.0.5",
                  "timestamp": "2017-03-29 05:52:02",
                  "language": "xx",
                  "latitude": "xx",
                  "longitude": "xx",
                  "observation_radius": "7",
                  "country": "xx",
                  "hour": "7",
                  "statpic_id": "4",
                  "last_mail": "2017-03-29 07:52:02",
                  "last_login": "2017-03-14 22:04:05",
                  "secid": "xx",
                  "retrieve_token": null,
                  "retrieve_expiration": null
                }
              },
              "parentModels": false,
              "alias": null,
              "refModelName": "Users"
            }
          }
        }
      ],
      "meta": {
        "stopwatch": {
          "total_run_time": "10.91 ms",
          "laps": {
            "Booting App": "0.11 ms",
            "Loading Routes": "0.63 ms",
            "Processing Request": "7.38 ms",
            "Gather Records": "2.79 ms"
          }
        }
      }
    }
    Notice: ob_clean(): failed to delete buffer. No buffer to delete in /var/www/vendor/gte451f/phalcon-json-api-package/src/bin/errorHandler.php on line 65
    
    Fatal error: Uncaught Phalcon\Http\Response\Exception: Response was already sent in phalcon/http/response.zep:611
    Stack trace:
    #0 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/Output.php(103): Phalcon\Http\Response->send()
    #1 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/Output.php(73): PhalconRest\API\Output->_send(Object(stdClass))
    #2 /var/www/vendor/gte451f/phalcon-json-api-package/src/bin/errorHandler.php(95): PhalconRest\API\Output->send(Object(PhalconRest\Result\Adapters\JsonApi\Result))
    #3 [internal function]: {closure}()
    #4 {main}
      thrown in phalcon/http/response.zep on line 611
    
    
    opened by kumy 0
  • Restrict parsing url parameters to $_GET

    Restrict parsing url parameters to $_GET

    In some circumstances, the request may have (in my case) some cookies defined, which are interpreted by phalcon-json-api-package as columns to filter.

    Framework exception caught: Column 'PHPSESSID' doesn't belong to any of the selected models (1), when preparing: SELECT count(*) as count FROM [PhalconRest\Models\Pictures] WHERE [PHPSESSID] = :rand311256:
    #0 [internal function]: Phalcon\Mvc\Model\Query->_getQualified(Array)
    #1 [internal function]: Phalcon\Mvc\Model\Query->_getExpression(Array, true)
    #2 [internal function]: Phalcon\Mvc\Model\Query->_getExpression(Array)
    #3 [internal function]: Phalcon\Mvc\Model\Query->_prepareSelect()
    #4 [internal function]: Phalcon\Mvc\Model\Query->parse()
    #5 [internal function]: Phalcon\Mvc\Model\Query->execute(NULL, NULL)
    #6 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/Entity.php(359): Phalcon\Mvc\Model\Query->getSingleResult()
    #7 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/Entity.php(168): PhalconRest\API\Entity->runSearch()
    #8 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/BaseController.php(170): PhalconRest\API\Entity->find()
    #9 [internal function]: PhalconRest\API\BaseController->get()
    #10 [internal function]: Phalcon\Mvc\Micro\LazyLoader->__call('get', Array)
    #11 [internal function]: Phalcon\Mvc\Micro\LazyLoader->callMethod('get', Array, NULL)
    #12 /var/www/public/index.php(41): Phalcon\Mvc\Micro->handle()
    #13 {main}
    

    or

    Framework exception caught: Scanning error before 'ember_simple_aut...' when parsing: SELECT count(*) as count FROM [PhalconRest\Models\News] WHERE [ember_simple_auth-session] = :rand759899: (104)
    #0 [internal function]: Phalcon\Mvc\Model\Query->parse()
    #1 [internal function]: Phalcon\Mvc\Model\Query->execute(NULL, NULL)
    #2 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/Entity.php(359): Phalcon\Mvc\Model\Query->getSingleResult()
    #3 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/Entity.php(168): PhalconRest\API\Entity->runSearch()
    #4 /var/www/vendor/gte451f/phalcon-json-api-package/src/API/BaseController.php(170): PhalconRest\API\Entity->find()
    #5 [internal function]: PhalconRest\API\BaseController->get()
    #6 [internal function]: Phalcon\Mvc\Micro\LazyLoader->__call('get', Array)
    #7 [internal function]: Phalcon\Mvc\Micro\LazyLoader->callMethod('get', Array, NULL)
    #8 /var/www/public/index.php(41): Phalcon\Mvc\Micro->handle()
    #9 {main}
    
    
    opened by kumy 0
  • Maintenance mode, bunch of small fixes

    Maintenance mode, bunch of small fixes

    7985278 (Igor Santos, 18 seconds ago) Displaying JSON errors on invalid request bodies

    f865773 (Igor Santos, 46 seconds ago) Adding a Maintenance Mode setting on config.application.maintenance - returns 503 on any request, and dies early on CLI calls

    1698bab (Igor Santos, 11 minutes ago) Forcing HTTP Exceptions to display both their codes so that's searchable from Sentry

    2932c88 (Igor Santos, 2 days ago) Adding the correct HTTP code to its ErrorStore object

    7d847a6 (Jim, 5 weeks ago) bug fix that surfaced on JSON API

    c254809 (Igor Santos, 11 days ago) Enabling usage of array data in model save() calls as well

    5c1f081 (Igor Santos, 3 weeks ago) Single relationshop results are now set as simple values instead of a one-entry array (that could override simple fields from the resultset). IDs are also integers whenever possible

    opened by igorsantos07 0
  • Code improvements, removal of unused function, 204 improvements

    Code improvements, removal of unused function, 204 improvements

    • Bunch of small code improvements (mostly PHPDoc)
    • removal of an unused function (@wtf annotation + PHPStorm told me about it)
    • improvement on 204 handling
    opened by igorsantos07 0
  • mungeData is not mandatory, and other small improvements

    mungeData is not mandatory, and other small improvements

    • mungeData is not mandatory anymore, as it's not always needed
    • getJson() now is future-proof for nullable types, returning null instead of false in case of issues
    • getPut() obeys it's parent signature
    • plain[] handling was fixed
    • a bunch of code style improvements
    opened by igorsantos07 0
  • Object Level Security

    Object Level Security

    Let's add in some security tools that allow developers to more easily and reliably apply security rules in common situations. The design goal is to make it possible for developers to define a minimum set of rules that will enforce security rules in a variety of situations.

    Rules

    • READ Rules when getting 1 or more records from an end point
      • reuse filter rules when side loading data
    • Block/Allow rules to Edit/Delete/Insert records

    Implement Edit/Delete/Insert records as hooks before the operation is performed. Can we ready in these rules from a common syntax or from the READ rules?

    opened by gte451f 0
  • Requesting a relation by Model name

    Requesting a relation by Model name

    When requesting a relation by model name and a model have multiple relation to the same class, the result only display the first element.

    class News extends \PhalconRest\API\BaseModel
    {
    [...]
        public function initialize()
        {
            parent::initialize();
    
            $this->belongsTo('user_id', Users::class, 'id', ['alias' => 'author']);
            $this->belongsTo('reviewer_id', Users::class, 'id', ['alias' => 'reviewer']);
        }
    [...]
    }
    

    Result:

      "relationships": {
        "user": {
          "data": {
            "id": "1",
            "type": "users"
          }
        }
      }
    

    Even with the fix (PR is comming) for #52 result only display first entry.

    opened by kumy 0
  • Requesting a relation by alias raise error

    Requesting a relation by alias raise error

    When selecting a relation by "alias", an exception is raised:

    http://127.0.0.1/v1/news/174?with=author

    {
    "errors": [
    {
    "title": "Unexpected Error",
    "code": 0,
    "detail": "Call to a member function getType() on string",
    "meta": {
    "file": "/var/www/vendor/gte451f/phalcon-json-api-package/src/Result/Data.php",
    "line": 111,
    "stack": [
    {
    "file": "/var/www/vendor/gte451f/phalcon-json-api-package/src/API/Entity.php",
    "line": 992,
    "function": "addRelationship",
    "class": "PhalconRest\\Result\\Data",
    "type": "->",
    "args": [
    "user",
    "1",
    "users"
    ]
    },
    {
    "file": "/var/www/vendor/gte451f/phalcon-json-api-package/src/API/Entity.php",
    "line": 497,
    "function": "loadRelationRecords",
    "class": "PhalconRest\\API\\Entity",
    "type": "->",
    "args": [
    [...]
    
    opened by kumy 0
  • Mess when multiple relation to the same class

    Mess when multiple relation to the same class

    Let's assume the following relation:

    class News extends \PhalconRest\API\BaseModel
    {
    [...]
        public function initialize()
        {
            parent::initialize();
    
            $this->belongsTo('user_id', Users::class, 'id', ['alias' => 'author']);
            $this->belongsTo('reviewer_id', Users::class, 'id', ['alias' => 'reviewer']);
        }
    [...]
    }
    

    A request to http://127.0.0.1/v1/news/174?with=all result in:

    {
      "data": {
        "id": "174",
        "type": "news",
        "attributes": {
          "user-id": "1",
          "reviewer-id": "2"
        },
        "relationships": {
          "user": {
            "data": {
              "0": {
                "id": "2",
                "type": "users"
              },
              "id": "1",
              "type": "users"
            }
          }
        }
      }
    }
    

    I see two problems here.

    First the relationship data is wrongly formatted. For consistency, it should be :

     "relationships": {
       "user": {
         "data": {
           "0": {
             "id": "2",
             "type": "users"
           },
           "1": {
             "id": "1",
             "type": "users"
           }
         }
       }
     }
    

    Secondly Having the first level below "relationships" defined with the related class name (ie: "user") doesn't seems to respect the json api specification.

    • See for example http://jsonapi.org/examples/ the relationship is defined as:
        "relationships": {
          "author": {
            "data": {"id": "42", "type": "people"}
          }
        }
    
    • Back to our example here, I expect the result to be:
     "relationships": {
       "author": {
         "data": {
           "id": "1",
           "type": "users"
         }
       },
       "reviewer": {
         "data": {
           "id": "2",
           "type": "users"
         }
       }
     }
    
    opened by kumy 0
Releases(3.2.11)
Owner
Jim
Jim
YCOM Impersonate. Login as selected YCOM user 🧙‍♂️in frontend.

YCOM Impersonate Login as selected YCOM user in frontend. Features: Backend users with admin rights or YCOM[] rights, can be automatically logged in v

Friends Of REDAXO 17 Sep 12, 2022
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
Composer registry manager that help to easily switch to the composer repository you want

CRM - Composer Registry Manager Composer Registry Manager can help you easily and quickly switch between different composer repositories. 简体中文 Install

Tao 500 Dec 29, 2022
A Phalcon paginator adapter for Phalcon Collections

Phalcon Collection Paginator A Phalcon paginator adapter for Phalcon Collections Why create this? Being familiar with the various Pagination data adap

Angel S. Moreno 2 Oct 7, 2022
A composer plugin, to install differenty types of composer packages in custom directories outside the default composer default installation path which is in the vendor folder.

composer-custom-directory-installer A composer plugin, to install differenty types of composer packages in custom directories outside the default comp

Mina Nabil Sami 136 Dec 30, 2022
Dependency graph visualization for composer.json (PHP + Composer)

clue/graph-composer Graph visualization for your project's composer.json and its dependencies: Table of contents Usage graph-composer show graph-compo

Christian Lück 797 Jan 5, 2023
Ied plugin composer - Inspired Plugin Composer: Create, publish and edit plugins from within Textpattern CMS.

ied_plugin_composer Create, publish and edit plugins from within Textpattern CMS. Creates a new page under the Extensions tab where you can edit and e

Stef Dawson 8 Oct 3, 2020
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
Greyhole uses Samba to create a storage pool of all your available hard drives, and allows you to create redundant copies of the files you store.

Greyhole Greyhole is an application that uses Samba to create a storage pool of all your available hard drives (whatever their size, however they're c

Guillaume Boudreau 245 Dec 18, 2022
Type and shape system for arrays. Help write clearer code when implementing configs for your PocketMine-MP plugin or composer project.

ConfigStruct Type and shape system for arrays. Help write clearer code when implementing configs for your PocketMine-MP plugin or composer project. It

EndermanbugZJFC 9 Aug 22, 2022
Magento-composer-installer - Composer installer for Magento modules

!!! support the maintainer of this project via Patreon: https://www.patreon.com/Flyingmana Magento Composer Installer The purpose of this project is t

null 213 Sep 24, 2022
Composer Repository Manager for selling Magento 2 extension and offering composer installation for ordered packages.

Magento 2 Composer Repository Credits We got inspired by https://github.com/Genmato. Composer Repository for Magento 2 This extension works as a Magen

EAdesign 18 Dec 16, 2021
Composer plugin that wraps all composer vendor packages inside your own namespace. Intended for WordPress plugins.

Imposter Plugin Composer plugin that wraps all composer vendor packages inside your own namespace. Intended for WordPress plugins. Built with ♥ by Typ

Typist Tech 127 Dec 17, 2022
Composer Registrar Composer Plugin for Magento 2

This module add a global registration.php that replace the default glob search performed for each request to discover the components not installed from composer.

OpenGento 3 Mar 22, 2022
Drupal Composer Scaffold - A flexible Composer project scaffold builder

This project provides a composer plugin for placing scaffold files (like index.php, update.php, …) from the drupal/core project into their desired location inside the web root. Only individual files may be scaffolded with this plugin.

Drupal 44 Sep 22, 2022
Victor The Cleaner for Composer - This tool removes unnecessary files and directories from Composer vendor directory.

Victor The Cleaner for Composer This tool removes unnecessary files and directories from Composer vendor directory. The Cleaner leaves only directorie

David Grudl 133 Oct 26, 2022
Opinionated version of Wikimedia composer-merge-plugin to work in pair with Bamarni composer-bin-plugin.

Composer Inheritance Plugin Opinionated version of Wikimedia composer-merge-plugin to work in pair with bamarni/composer-bin-plugin. Usage If you are

Théo FIDRY 25 Dec 2, 2022
Checks prefer-lowest installation for actually defined min versions in composer.json

Composer Prefer Lowest Validator This validator will strictly compare the specified minimum versions of your composer.json with the ones actually used

Mark Scherer 17 Aug 7, 2022