• This repository has been archived on 31/Jan/2022
  • Stars
    star
    134
  • Rank 270,967 (Top 6 %)
  • Language
    PHP
  • License
    MIT License
  • Created almost 13 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Silex, Propel, Backbone, Stack, Hateoas, Negotiation, and so on!

Propilex

Build Status

A Silex application which uses Propel, Backbone.JS, but also:

And:

This project also features:

Installation

Install PHP dependencies:

composer install

And browser dependencies using Bower:

bower install

Build Model classes, SQL, and Propel's configuration:

cp app/config/propel/runtime-conf.xml.dist app/config/propel/runtime-conf.xml
cp app/config/propel/build.properties.dist app/config/propel/build.properties
bin/bootstrap

You're done! You can run the application using the PHP built-in webserver:

php -S 0.0.0.0:4000 -t web/

Open http://localhost:4000/ in your browser to see Propilex running.

Docker

Build the container:

docker build -t propilex .

Run it:

docker run -itP propilex

Retrieve the public port mapped to the container's port 80:

docker port $(docker ps -aql 1) 80

Open http://localhost:<port>/, and profit!

Usage

This application is a truely RESTful API with hypermedia links, content negotiation (format and language) but also cache on safe methods. The API is HAL compliant, and serves content in either XML or JSON format, using the most appropriate language (English, French, etc.) based on clients' preferences.

You can use the web interface, or the command line and tools such as HTTPie or cURL.

GET

You can get either a set of documents, or a single document. The response for the set of documents is cacheable, relying on the ETag headers.

Getting all documents in JSON:

$ http http://localhost:4000/documents Accept:application/hal+json
HTTP/1.1 200 OK
Cache-Control: public
Content-Type: application/hal+json
ETag: "c4ca4238a0b923820dcc509a6f75849b"
{
    "_links": {
        "curies": [
            {
                "href": "http://localhost:4000/rels/{rel}",
                "name": "p",
                "templated": true
            }
        ],
        "p:documents": {
            "href": "http://localhost:4000/documents"
        },
        "first": {
            "href": "http://localhost:4000/documents?page=1&limit=10"
        },
        "last": {
            "href": "http://localhost:4000/documents?page=1&limit=10"
        },
        "self": {
            "href": "http://localhost:4000/documents?page=1&limit=10"
        }
    },
    "_embedded": {
        "documents": [
            {
                "_links": {
                    "self": {
                        "href": "http://localhost:4000/documents/1"
                    },
                    "curies": [
                        {
                            "href": "http://localhost:4000/rels/{rel}",
                            "name": "p",
                            "templated": true
                        }
                    ]
                },
                "body": "Hello, World!",
                "created_at": "2013-12-22 17:55:18",
                "id": 1,
                "title": "Hello!",
                "updated_at": "2013-12-22 17:55:18"
            },
            {
                "_links": {
                    "self": {
                        "href": "http://localhost:4000/documents/2"
                    },
                    "curies": [
                        {
                            "href": "http://localhost:4000/rels/{rel}",
                            "name": "p",
                            "templated": true
                        }
                    ]
                },
                "body": "This is a body",
                "created_at": "2013-12-22 17:55:22",
                "id": 2,
                "title": "This is a title",
                "updated_at": "2013-12-22 22:09:37"
            }
        ]
    },
    "limit": 10,
    "page": 1,
    "pages": 1
}

Getting all documents in XML:

$ http http://localhost:4000/documents Accept:application/hal+xml
HTTP/1.1 200 OK
Cache-Control: public
Content-Type: application/hal+xml
ETag: "c4ca4238a0b923820dcc509a6f75849b"
<?xml version="1.0" encoding="UTF-8"?>
<collection href="http://localhost:4000/documents?page=1&amp;limit=10" limit="10" page="1" pages="1">
    <resource href="http://localhost:4000/documents/1" rel="documents">
        <id>1</id>
        <title>Hello!</title>
        <body>Hello, World!</body>
        <created_at><![CDATA[2013-12-22 17:55:18]]></created_at>
        <updated_at><![CDATA[2013-12-22 17:55:18]]></updated_at>
        <link href="http://localhost:4000/rels/{rel}" name="p" rel="curies" templated="1"/>
    </resource>
    <resource href="http://localhost:4000/documents/2" rel="documents">
        <id>2</id>
        <title>This is a title</title>
        <body>This is a body</body>
        <created_at><![CDATA[2013-12-22 17:55:22]]></created_at>
        <updated_at><![CDATA[2013-12-22 22:09:37]]></updated_at>
        <link href="http://localhost:4000/rels/{rel}" name="p" rel="curies" templated="1"/>
    </resource>
    <link href="http://localhost:4000/documents?page=1&limit=10" rel="first"></link>
    <link href="http://localhost:4000/documents?page=1&limit=10" rel="last"></link>

    <link href="http://localhost:4000/rels/{rel}" name="p" rel="curies" templated="1"/>
    <link href="http://localhost:4000/documents" rel="p:documents"/>
</collection>

Getting a single document in JSON:

$ http http://localhost:4000/documents/1 Accept:application/hal+json
HTTP/1.1 200 OK
Content-Type: application/hal+json
{
    "_links": {
        "self": {
            "href": "http://localhost:4000/documents/1"
        },
        "curies": [
            {
                "href": "http://localhost:4000/rels/{rel}",
                "name": "p",
                "templated": true
            }
        ]
    },
    "body": "Hello, World!",
    "created_at": "2013-12-22 17:55:18",
    "id": 1,
    "title": "Hello!",
    "updated_at": "2013-12-22 22:41:55"
}

Getting a single document in XML:

$ http http://localhost:4000/documents/1 Accept:application/hal+xml
HTTP/1.1 200 OK
Content-Type: application/hal+xml
<?xml version="1.0" ?>
<document href="http://localhost:4000/documents/1">
    <id>1</id>
    <title><![CDATA[Hello!]]></title>
    <body><![CDATA[Hello, World!]]></body>
    <created_at><![CDATA[2013-12-22 17:55:18]]></created_at>
    <updated_at><![CDATA[2013-12-22 22:41:55]]></updated_at>
    <link href="http://localhost:4000/rels/{rel}" name="p" rel="curies" templated="1"/>
</document>

POST

You can create a new document by sending JSON data:

$ curl -H 'Accept: application/hal+json' -H 'Content-Type: application/json' \
    -d '{"title": "Hello!", "body": "JSON"}' \
    http://localhost:4000/documents
HTTP/1.1 201 Created
Content-Type: application/hal+json
Location: http://localhost:4000/documents/7"
{
    "id": 7,
    "title": "Hello!",
    "body": "JSON",
    "created_at": "2013-12-22 22:48:46",
    "updated_at": "2013-12-22 22:48:46",
    "_links": {
        "self": {
            "href": "http://localhost:4000/documents/7"
        },
        "curies": [
            {
                "href": "http://localhost:4000/rels/{rel}",
                "name": "p",
                "templated": true
            }
        ]
    }
}

Creating a new document is also doable by sending XML data:

$ curl -H 'Accept: application/hal+json' -H 'Content-Type: application/xml' \
    -d '<document><title>Hello!</title><body>XML</body></document>' \
    http://localhost:4000/documents
HTTP/1.1 201 Created
Content-Type: application/hal+json
Location: http://localhost:4000/documents/8"
{
    "id": 8,
    "title": "Hello!",
    "body": "XML",
    "created_at": "2013-12-22 22:50:46",
    "updated_at": "2013-12-22 22:50:46",
    "_links": {
        "self": {
            "href": "http://localhost:4000/documents/8"
        },
        "curies": [
            {
                "href": "http://localhost:4000/rels/{rel}",
                "name": "p",
                "templated": true
            }
        ]
    }
}

### DELETE

$ http DELETE http://localhost:4000/documents/1
HTTP/1.1 204 No Content

If the document you are trying to delete does not exist, you will get an error:

$ http DELETE http://localhost:4000/documents/70 Accept:application/hal+json
HTTP/1.1 404 Not Found
Content-Type: application/vnd.error+json
{
    "message": "Document with id = 70 does not exist."
}

XML response for this error:

$ http DELETE http://localhost:4000/documents/70 Accept:application/hal+xml
HTTP/1.1 404 Not Found
Content-Type: application/vnd.error+xml
<?xml version="1.0" ?>
<resource>
    <message><![CDATA[Document with id = 70 does not exist.]]></message>
</resource>

Translations & Error Messages

Both error messages or application's messages are translated depending on the Accept-Language header. In order to implement this, you need to use the StackNegotiation middleware, and a Silex application's before middleware.

A response with a status code equals to either 404 or 500 follows the vnd.error specification.

You will get an error message if you try to get an unknown document:

$ http GET http://localhost:4000/documents/123 Accept:application/hal+json Accept-Language:en
HTTP/1.1 404 Not Found
Content-Type: application/vnd.error+json
{
    "message": "Document with id = \"123\" does not exist."
}

XML response for this error:

$ http GET http://localhost:4000/documents/123 Accept:application/hal+xml Accept-Language:fr
HTTP/1.1 404 Not Found
Content-Type: application/vnd.error+xml
<?xml version="1.0" ?>
<resource>
    <message><![CDATA[Le document avec id = "123" n'existe pas.]]></message>
</resource>

You will get an error message if you submit invalid data in order to create or update documents:

$ http POST http://localhost:4000/documents Accept:application/hal+json Accept-Language:fr
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
    "errors": [
        {
            "field": "title",
            "message": "Cette valeur ne doit pas être vide."
        },
        {
            "field": "body",
            "message": "Cette valeur ne doit pas être vide."
        }
    ]
}

XML response for this error:

$ curl -H 'Accept: application/hal+xml' -H 'Content-Type: application/json' \
    -d '{"title": "Hello!"}' \
    http://localhost:4000/documents
HTTP/1.1 400 Bad Request
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<errors>
    <error field="body">
        <message><![CDATA[This value should not be blank.]]></message>
    </error>
</errors>

If you send an Accept header with an unsupported mime type, you will get a 406 error:

$ http GET http://propilex.herokuapp.com/documents Accept:application/json
HTTP/1.1 406 Not Acceptable
Content-Type: text/plain

Mime type "application/json" is not supported. Supported mime types are:
application/hal+xml, application/hal+json.

Configuration

All configuration files are located in the app/config/ directory.

  • propel/runtime-conf.xml and propel/build.properties contain the database configuration, if you modify it, don't forget to rebuild things by using the previous command;
  • serializer/* contains the Serializer and Hateoas configuration;
  • messages.*.yml contain the translations;
  • validation.yml contains the Validation configuration.

You can also find a few parameters in app/propilex.php.

Screenshots

Deploy on Heroku

Create a new Heroku application:

heroku create --buildpack https://github.com/CHH/heroku-buildpack-php myapp

Deploy it!

git push heroku master

Unit Tests

First, install the application as described in section Installation.

Backend

Install dev dependencies:

composer install --dev

Then, run the test suite:

bin/phpunit

Frontend

In a browser, open /js/tests/index.html.

In a shell, install PhantomJS, and run the following comand:

phantomjs web/js/tests/run-qunit.js file://`pwd`/web/js/tests/index.html

License

Propilex is released under the MIT License. See the bundled LICENSE file for details.

More Repositories

1

Negotiation

Content Negotiation tools for PHP.
PHP
1,371
star
2

Hateoas

A PHP library to support implementing representations for HATEOAS REST web services.
PHP
1,016
star
3

JsonpCallbackValidator

JSONP callback validator.
PHP
653
star
4

EmailReplyParser

PHP library for parsing plain text email content.
PHP
621
star
5

BazingaJsTranslationBundle

A pretty nice way to expose your Symfony translation messages to your client applications.
JavaScript
574
star
6

ArvernOS

💾 A minimal, experimental and "toy" monolithic kernel to learn about OS development // Work In Progress
C
325
star
7

BazingaHateoasBundle

Integration of the Hateoas library into Symfony.
PHP
290
star
8

BazingaFakerBundle

Put the awesome Faker library into the Symfony2 DIC and populate your database with fake data.
PHP
279
star
9

docker-elk

🐳 Creating an ELK stack could not be easier.
270
star
10

nmap

nmap is a PHP wrapper for Nmap.
PHP
154
star
11

willdurand.github.io

William Durand's website ✨
HTML
147
star
12

anchorify.js

A dead simple JavaScript library for automatically creating anchored headings in your HTML documents.
JavaScript
117
star
13

BazingaRestExtraBundle

Provides extra features for your REST APIs built using Symfony.
PHP
95
star
14

puppet-nodejs

Puppet module to manage Node.js and NPM that just works.
Ruby
69
star
15

TravisLight

A build monitoring tool (buildwall) that allows you to quickly detect failing projects for Travis-CI.
JavaScript
64
star
16

rustwasm-addon

🦀 + 🕸 + 🦊 // A web-extension to reverse a string. Yep.
JavaScript
63
star
17

StateMachineBehavior

This behavior adds a finite state machine to your model.
PHP
38
star
18

EMD

Fast and Adaptive Bidimensional Empirical Mode Decomposition Using Order-Statistics Filter Based Envelope Estimation
C++
37
star
19

dotfiles

💻 My dotfiles.
Shell
36
star
20

hubot-cachet

A hubot script to manage incidents/statuses with Cachet.
CoffeeScript
34
star
21

BazingaOAuthServerBundle

[Symfony2] A server side implementation of the OAuth 1.0 protocol based on RFC 5849.
PHP
31
star
22

puppet-composer

Puppet module to install Composer.
Ruby
30
star
23

StackNegotiation

Stack middleware for content negotiation.
PHP
27
star
24

docker-logstash-forwarder

Docker image for Logstash Forwarder, formerly known as lumberjack.
25
star
25

TypehintableBehavior

Insane Propel behavior that helps you to be compliant with third-party interfaces by adding type hint to generated methods.
PHP
22
star
26

pihole-oled

💻 Pi-hole and system stats displayed on an OLED 0.96" screen.
Python
22
star
27

jenairienacacher.fr

Site informatif autour de l'argument "je n'ai rien à cacher".
HTML
22
star
28

pman

"pear-doc.php.net/pman" on Composer/Packagist!
Shell
20
star
29

EventDispatcherBehavior

Integrates the Symfony2 EventDispatcher component in your Model classes.
PHP
19
star
30

Speaker

Speaker aims to convert my blog posts in audio files.
Shell
16
star
31

EspWiFi

Arduino driver for the ESP8266 WiFi module.
C++
15
star
32

hubot-ansible

Let Hubot run `ansible-playbook` for you!
CoffeeScript
14
star
33

sonoff-webthing

💡 An alternative firmware to create "Things" with iTead Sonoff devices.
Makefile
13
star
34

pino-devtools

🌲 A transport for viewing logs in your favorite browser devtools!
JavaScript
13
star
35

jenkins-phing-symfony

Jenkins/Phing for symfony 1.x (Works with Hudson)
12
star
36

gitlab-elk-demo

A demo project showing how to integrate Gitlab with the ELK stack.
11
star
37

dynhost

📡 Automatically update a dynamic DNS record for an OVH domain.
Rust
11
star
38

workshop-rest-from-zero-to-hero

Workshop.
PHP
10
star
39

PublishableBehavior

The PublishableBehavior is designed to quickly add publish/unpublish features to your model.
PHP
10
star
40

container-registry-proxy

📖 A proxy that makes the GitHub Container Registry compatible with the Docker Registry HTTP API V2 specification.
Go
10
star
41

BazingaPropelEventDispatcherBundle

Integrates the Propel EventDispatcherBehavior into Symfony2.
PHP
10
star
42

docker-buildtools

9
star
43

chipolata

🌭 A CHIP-8 interpreter written in Rust.
Rust
9
star
44

clermontech-workshop-git-deploy

Material for my Git Workshop.
Shell
8
star
45

cwrm

📸 WiFi Remote Module ✨ // Work In Progress
Makefile
8
star
46

containers

📦 This is a repository with some code I wrote to learn more about containers.
Go
7
star
47

gitpod-firefox-dev

Firefox development with Gitpod = ❤️
Shell
6
star
48

gameboy-breakout-board

A GameBoy card slot breakout board.
OpenSCAD
6
star
49

Karotz-Plugin

A Jenkins Plugin for my beloved Karotz.
Java
6
star
50

puppet-bazinga

A set of Puppet roles and some useful functions.
Puppet
6
star
51

thesis

📝 Automated Test Generation for production systems with a Model-based Testing approach.
TeX
6
star
52

kicad-libs

William Durand's KiCad Libraries
OpenSCAD
6
star
53

DuckHunt

A little game in haXe.
Haxe
4
star
54

isomorphic-formdata

formdata-node + browser support = <3
JavaScript
4
star
55

StateMachineExporter

Generate a Graphviz compatible file (.dot file) from a Propel XML schema, and the StateMachineBehavior.
PHP
3
star
56

demo-pretty-printer

A demo project to complement my blog post about Pretty Printers.
JavaScript
3
star
57

amo-info

🦊 An extension to show information about AMO related services.
JavaScript
3
star
58

hardware-hacking-cast-device

Tools and design files related to my research on a cheap "cast device" clone.
C
3
star
59

uberdex

The official Uberdex!
2
star
60

puppet-stoplight

This module manages Stoplight, a powerful build monitoring tool.
Ruby
1
star
61

fx-attribution-data-reader

Attribution data reader for Firefox installers
JavaScript
1
star
62

drnd.me

CSS
1
star
63

universal-express-http-context

DEPRECATED: the work highlighted here has been merged into upstream.
JavaScript
1
star