Day 15: Web Services

With the addition of feeds on Jobeet, job seekers can now be informed of new
jobs in real-time.

On the other side of the fence, when you post a job, you will want to have the
greatest exposure possible. If your job is syndicated on a lot of small
websites, you will have a better chance to find the right person. That's the
power of the
long tail. Affiliates
will be able to publish the latest posted jobs on their websites thanks to the
web services we will develop along this day.

Creating records for the middle table of a many-to-many relationship is as
simple as defining an array with a key of the middle table name plus an s.
The content of the array is the object names as defined in the fixture files.
You can link objects from different files, but the names must be defined first.

In the fixtures file, tokens are hardcoded to simplify the testing, but when an
actual user applies for an account, the token will need to be generated:

For this route, the special sf_format variable ends the URL and the valid
values are xml, json, or yaml.

The getForToken() method is called when the action retrieves the collection of
objects related to the route. As we need to check that the affiliate is
activated, we need to override the default behavior of the route:

If the token does not exist in the database, we throw an sfError404Exception
exception. This exception class is then automatically converted to a
404 response. This is the simplest way to generate a 404 page
from a model class.

The getForToken() method uses two new methods we will create now.

First, the getByToken() method must be created to get an affiliate given its
token:

Instead of passing an array of JobeetJob objects to the templates, we pass an
array of strings. As we have three different templates for the same action, the
logic to process the values has been factored out in the JobeetJob::asArray()
method:

If you try to call the web service with a non-valid token, you will have a 404
XML page for the XML format, and a 404 JSON page for the JSON format. But for
the YAML format, symfony does not know what to render.

Whenever you create a format, a custom error template
must be created. The template will be used for 404 pages, and all other
exceptions.

As the exception should be different in the production and
development environment, two files are needed (config/error/exception.yaml.php
for debugging, and config/error/error.yaml.php for production):

To test the web service, copy the affiliate fixtures from data/fixtures/ to
the test/fixtures/ directory and replace the content of the auto-generated
apiActionsTest.php file with the following content:

It is a classic Propel collection route with a new configuration option:
actions. As we don't need all the seven default actions defined by the route,
the actions option instructs the route to only match for the new and
create actions. The additional wait route will be used to give the
soon-to-be affiliate some feedback about his account.

The propel:generate-module task generate the classic seven actions and their
corresponding templates. In the templates/ directory, remove all
the files but the _form.php and newSuccess.php ones. And for the files we
keep, replace their content with the following:

The new sfForm::useFields() method allows to specify the white list of fields
to keep. All non mentionned fields will be removed from the form.

The form framework supports many-to-many relationship like any other column. By default, such a relation is
rendered as a drop-down box thanks to the sfWidgetFormPropelChoice widget. As
seen during day 10, we have changed the rendered tag by using the expanded
option.

As emails and URLs tend to be quite longer than the default size of an input
tag, default HTML attributes can be set by using the setAttribute() method.

As the only action needed in the backend is to activate or deactivate accounts,
change the default generator config section to simplify the interface a bit
and add a link to activate accounts directly from the list view:

Thanks to the REST architecture of symfony, it is quite easy to implement web
services for your projects. Although, we wrote code for a read-only web service
today, you have enough symfony knowledge to implement a read-write web service.

The implementation of the affiliate account creation form in the frontend and
its backend counterpart was really easy as you are now familiar with the process
of adding new features to your project.

If you remember requirements from day 2:

"The affiliate can also limit the number of jobs to be returned, and refine
his query by specifying a category."

The implementation of this feature is so easy that we will let you do it
tonight.

Whenever an affiliate account is activated by the administrator, an email should
be sent to the affiliate to confirm his subscription and give him his token.
Sending emails is the topic we will talk about tomorrow.