athens / propel-js
Interact with your Propel database, in JavaScript.
Installs: 372
Dependents: 0
Suggesters: 0
Security: 0
Stars: 4
Watchers: 3
Forks: 1
Open Issues: 6
Type:propel-behavior
Requires
- propel/propel: ~2.0@dev
Requires (Dev)
- athens/standard: *
- codeclimate/php-test-reporter: dev-master
- phpdocumentor/phpdocumentor: 2.7.*
- phpunit/phpunit: 4.5.*
README
Interact with your Propel database, in JavaScript. PropelJS generates a JavaScript library from your Propel schema.
PropelJS is a behavior plugin for the Propel PHP ORM. You must be using Propel in order to use this package.
Example
Write the following JavaScript:
var db = bookstore.propelJS({baseAddress:'/api/'});
// Retrieve a book and log its title.
db.books(2).get().then(
function(book) {
console.log(book.getTitle());
}
// Create an author and a book they've written
db.authors()
.setFirstName('John')
.setLastName('Steinbeck Jr.')
.save()
.then(
function(author) {
db.books()
.setTitle('Grapes of Wrath II')
.setAuthorId(author.getId())
.save();
}
);
// Remove a book from the database.
db.books(5).delete();
Given the following schema:
<database name="bookstore" defaultIdMethod="native">
<table name="author" phpName="Author">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="first_name" type="varchar" size="128" required="true"/>
<column name="last_name" type="varchar" size="128" required="true"/>
</table>
<table name="book" phpName="Book">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="title" type="varchar" size="255" required="true" />
<column name="author_id" type="integer" required="true"/>
<foreign-key foreignTable="author">
<reference local="author_id" foreign="id"/>
</foreign-key>
</table>
<behavior name="propel_js" />
</database>
PropelJS creates the JavaScript library automatically every time you run propel model:build
.
How it Works
PropelJS is a Propel database behavior. Each time you run propel model:build
, it will generate a JavaScript library
and an API handler class.
In your backend, you create an API endpoint which invokes the ::handle
method of the API handler class. In your
frontend, you initialize a database connection to that endpoint. Then, all of your commands like db.authors(3).get();
are routed through the API handler and Propel's database connection.
Brief Setup Guide
The following steps assume you're using Composer and the LAMP stack. If you're not using Composer, then you can adapt these instructions to your own deployment environment.
-
Install PropelJS: Add
"athens/propel-js": "1.*"
to your Composer dependencies and runcomposer update
. -
Propel Schema: Add the
<behavior name="propel_js" />
between your<database></database>
tags. PropelJS is a database behavior, so it should not be placed inside<table></table>
tags. -
Build Your Models: Run
propel model:build
. This creates agenerated-api/API.php
file and agenerated-js/your-db-name.js
file. -
Add
API.php
to Autoload: Add"generated-api/"
to yourcomposer.json
autoloading, alongside"generated-classes/"
. -
Create an API Endpoint: You now need to create a web-accessible directory to serve as your API endpoint. For example, the
api/
directory could be your API endpoint using the followingapi/index.php
sample:require_once "/path/to/your/autoload.php"; use \YourPropelProjectNamespace\API; echo API::handle();
-
Request Routing: All requests to your API (eg:
api/authors/2
) need to be routed toapi/index.php
by your webserver. Depending on your server configuration, the followingapi/.htaccess
might work:RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ /absolute/path/to/api/index.php [L]
-
Include the JavaScript: You can either copy your
your-db-name.js
into a web accessible directory or you can configure your server to make thegenerated-js/
directory web accessible. In either case, you'll need to includeyour-db-name.js
and jQuery in your page headers:<script src="http://code.jquery.com/jquery-1.12.4.min.js"></script> <script src="/bookstore.js"></script>
-
Configure the Connection: Now you have to tell your JavaScript library where to find your API endpoint by configuring a database connection. For example:
var db = bookstore.propelJS({baseAddress:'/api/'});
You can read about more configuration options.
That's it! The db
variable is now your handle for communicating with the database. See
JavaScript Library Syntax for more information on how to use your auto generated
library.
Detailed Example
This example demonstrates the basic steps that you'll need to complete to use PropelJS. We'll use the LAMP stack plus Composer for dependency management.
The specific details of this example may or may not work on your server, depending on its configuration. And I've chosen some details that favor easy security over easy deployment; adapt this example to your own practices.
This example will use the following project structure:
├── composer.json
├── propel.inc
├── schema.xml
├── generated-classes/
├── generated-migrations/
├── generated-sql/
├── vendor/
└── webroot/
├── about.php
└── index.php
The webroot/
directory will serve as the root of our web domain, with index.php
and about.php
addressed as
http://example.net/index.php
and http://example.net/about.php
.
Step 1: Installation
This library is published on Packagist. To install using Composer, add the "athens/propel-js": "1.*"
line to
your "require"
section:
{
"require": {
...
"athens/propel-js": "1.*",
...
}
}
Step 2: Propel Schema
PropelJS is a database-level behavior for Propel. To use it, you must insert <behavior name="propel_js" />
into
your database schema. For example:
<database name="bookstore" defaultIdMethod="native">
<table name="author" phpName="Author">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="first_name" type="varchar" size="128" required="true"/>
<column name="last_name" type="varchar" size="128" required="true"/>
</table>
<table name="book" phpName="Book">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="title" type="varchar" size="255" required="true" />
<column name="author_id" type="integer" required="true"/>
<foreign-key foreignTable="author">
<reference local="author_id" foreign="id"/>
</foreign-key>
</table>
<behavior name="propel_js" />
</database>
Take note that <behavior name="propel_js" />
should be placed inside your <database></database>
tags, but not
inside your <table></table>
tags.
Step 3: Rebuild Your Models
Perform a propel model:build
as usual.
You should now have a generated-api/
directory and a generated-js/
directory living alongside your usual
generated-classes/
directory.
├── composer.json
├── propel.inc
├── schema.xml
├── generated-api/ <- New
│ └── API.php <- New
├── generated-classes/
├── generated-js/ <- New
│ └── bookstore.js <- New
├── generated-migrations/
├── generated-sql/
├── vendor/
└── webroot/
├── about.php
└── index.php
Step 4: Add API.php to Autoload
If you're using Composer or any other autoloading scheme, then you need to add the API
class inside API.php
to that
autoloader. For Composer, add the generated-api/
directory to the autoload
section of your composer.json
. For example:
...
"autoload": {
"classmap": [
"generated-classes/",
"generated-api/",
]
}
...
Step 5: Create an API Endpoint
Create an api
directory and an index.php
to serve API requests.
├── composer.json
├── propel.inc
├── schema.xml
├── generated-api/
│ └── API.php
├── generated-classes/
├── generated-js/
│ └── bookstore.js
├── generated-migrations/
├── generated-sql/
├── vendor/
└── webroot/
├── api/ <- This directory
│ ├── .htaccess <- This file
│ └── index.php <- And this file
├── about.php
└── index.php
<?php
/** api/index.php */
require_once dirname(__FILE__) ."/../vendor/autoload.php";
/**
* The 'Bookstore' namespace comes from our database schema definition.
* Your project will probably have a different namespace.
*/
echo \Bookstore\API::handle();
For your own project, the namespace for API
won't be Bookstore
. Change Bookstore
to the namespace of your Propel
files. If in doubt, check the namespace declaration in generated-api/API.php
.
The .htaccess
file is explained in the next step.
Step 6: Request Routing
In order to serve API requests such as GET /api/authors/2
, we have to tell Apache to direct any requests within the
'api/' directory to the 'api/index.php' file.
If you're using an Apache web server, then this can be accomplished with a .htaccess
file. The following api\.htaccess
example is likely to work on your server:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# You may need to change '/api/index.php' to the actual absolute address of your API index.
RewriteRule ^(.*)$ /api/index.php [L]
However this may not work on your particular server: your server might not have mod-rewrite, or your Apache config might not allow you to use it on your site.
Consult your local .htaccess
guru if you need help.
Step 7: Include the JavaScript
In order for our web pages to include the bookstore.js
, it has to be accessible to the web. There are two ways to
accomplish this:
- Configure your server/project so that the
generated-js/
directory is accessible to the web. - Copy the
bookstore.js
file into a web-accessible directory.
I would normally choose (1) to ease deployment, but for demonstration purposes we'll demonstrate (2) by copying
bookstore.js
into the webroot/
directory:
├── composer.json
├── propel.inc
├── schema.xml
├── generated-api/
│ └── API.php
├── generated-classes/
├── generated-js/
│ └── bookstore.js
├── generated-migrations/
├── generated-sql/
├── vendor/
└── webroot/
├── api/
│ ├── .htaccess
│ └── index.php
├── bookstore.js <- Paste bookstore.js here
├── about.php
└── index.php
Now bookstore.js
is addressable as http://example.net/bookstore.js
, and you can include it in the head
of your html files:
<head>
...
<!-- PropelJS requires JQuery -->
<script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="/bookstore.js"></script>
...
</head>
Step 8: Configure a Connection
Finally, we configure a PropelJS connection. This can go right below the <script src="/bookstore.js"></script>
tag
we created in Step 7:
<head>
...
<!-- PropelJS requires JQuery -->
<script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="/bookstore.js"></script>
<script type="text/javascript">
var db = bookstore.propelJS({baseAddress:'/api/'});
</script>
...
</head>
That's it! Now you can create, retrieve, update, and delete authors and books as in the example above.
Connection Configuration
In the examples above, we use var db = bookstore.propelJS({baseAddress:'/api/'});
to create a database connection.
The dictionary {baseAddress:'/api/'}
represents the configuration options used to create the connection, but
baseAddress
is not the only available option.
The available options are:
The headers
option would typically be used to send authentication headers for a CSRF or OAuth protected API. For
example:
var db = bookstore.propelJS(
{
baseAddress: '/api/',
headers: {
'CSRF-TOKEN': 'c06pmdts636djbbe'
}
}
);
JavaScript Library Syntax
The PropelJS interface is built on method cascading and jQuery promises.
Creating and manipulating instances:
// Configure a connection to the database.
var db = bookstore.propelJS({baseAddress:'/api/'});
// Create a new author
var myAuthor = bookstore.authors();
// Set the author details:
myAuthor.setFirstName('Sam');
myAuthor.setLastName('Morgan');
// Create a new author and set details using method cascading:
var myAuthor2 = bookstore.authors()
.setFirstName('Terri')
.setLastName('Johns');
// Create a book:
var myBook = bookstore.books()
.setTitle('Cold Winds');
Saving to the database:
myAuthor.save();
Each instance has a get
, save
, and delete
method. These database I/O methods return jQuery promises for asynchronous
response handling. For example:
// Save myAuthor2 to the database. When that is done, retrieve the author id
// returned by the database and assign it to the AuthorID of myBook. Then save
// myBook.
myAuthor2.save()
.then(
function(author) {
myBook.setAuthorId(author.getId()).save();
}
);
Retrieving an instance from the database:
// Retrieve the author with id 4, then log their name
db.authors(4)
.get()
.then(
function(author) {
console.log(author.getFirstName());
}
);
Deleting an instance from the database:
db.authors(4)
.delete()
.then(
function() {
console.log("Deleted an author.")
}
);
You can retrieve multiple instances from the database by doing a GET without specifying an ID. Here, we search for all authors with the first name John. The resulting collection authors
supports a jQuery style each
:
db.authors()
.setFirstName('John')
.get()
.then(
function(authors) {
authors.each(function(author) {
console.log('Found author John ' + author.getLastName() + '.');
});
}
);
Compatibility
- PHP 5.5, 5.6, 7.0
- Propel 2
Todo
See GitHub issue tracker.
Getting Involved
Feel free to open pull requests or issues. GitHub is the canonical location of this project.
Here's the general sequence of events for code contribution:
- Open an issue in the issue tracker.
- In any order: Submit a pull request with a failing test that demonstrates the issue/feature. Get acknowledgement/concurrence.
- Revise your pull request to pass the test in (2). Include documentation, if appropriate.
PSR-2 compliance is enforced by CodeSniffer in Travis.