Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
How I built web services in CakePHP
1. How I built web services in CakePHP
Amsterdam
2016
2. Introduction
PHP Developer
at UK Web Media
in Weyhill, UK
http://www.ukwm.co.uk
Cyberstalk me!
https://twitter.com/YellDavid
https://github.com/davidyell
http://careers.stackoverflow.com/davidyell
http://uk.linkedin.com/in/davidyell
#cakephp on Freenode IRC
David Yell
aka Neon1024
9. Setup
/api/v2
Yey!
●
Shares existing code
●
More generic
● Extensibility
● Per endpoint customisation
Boo!
●
Hard to create common filtering
● Too much code in AppController
●
Lacks separation
Routing
Request
Response
Application
Version 2
v2 prefix
AppController ProductsController
10. API Design choices
Pragmatic approach
Complex queries but more complete responses
Allows complete configured return
Flexible filtering and sorting
Specific use-cases
Further reading,
http://www.bravo-kernel.com/tags/api/
11. Version 3
Routing
Request
Response
api prefix
v3 route
ApiAppController
Crud plugin
Custom Crud actions
Contain class Filter class
Yey!
● Crud automation
● Free sorting and pagination
● Code separation
● Extensible
Boo!
● Complexity
Setup
/api/v3
ApiQuery Behavior
18. Version 3
Example queries
Get packages with related data, where line rental is included,
from provider id 4, matching an associated Table field, sorted
by price, on page 2.
/api/v3/packages.json
?verbose=true
&filter[line_rental_included]=true
&filter[provider_id]=4
&filter[Related.something]=foo
&site_id=n
&limit=20
&page=3
&sort=price
&direction=asc
Free with Crud plugin!
19. Version 3
Example queries
Get a specific list of packages in a certain order.
/api/v3/packages.json
?id[0]=19
&id[1]=17
&id[2]=11
&id[3]=43
&site_id=n
20. Lessons learned
Don't
● Single controller
● Filter in controller
Do
● Routing prefixes
● Crud plugin
● Authentication
● Create separation
● Integration testing
22. Consuming the API
Cake 2
Custom data source
http://book.cakephp.org/2.0/en/models/datasources.html
23. Muffin/WebService Cake 3 driver
http://jedistirfry.co.uk/blog/2015-09/connecting-to-a-web-service/
Consuming the API
Cake 3
24. Thanks!
Questions?
Please rate my talk
https://joind.in/talk/e2065
Attend your local PHP Usergroup! http://php.ug/
http://osmihelp.org/donate
Notes de l'éditeur
Thanks for voting on my talk, I really appreciate it.
Nearly 10 years commercial experience, mostly agencies doing campaign work.
Started with HTML on GeoCities.
Self-taught in PHP, started with CakePHP on 1.3 in 2008 ish and am in the Friends of Cake group
UKWM do campaign and affiliate marketing work, I work mostly on the telecoms sites comparing broadband, tv and phone
All our stuff is CakePHP 2 or 3, I'm migrating stuff as and when I have time
This is the first run of this presentation so be gentle!
I will hopefully have some time at the end for questions.
It's what I do for my job
v1 is the first api I ever built!
It's a read only web service
Built by myself, used by myself
Work in progress
We are silo'd at work, so you guys are doing my code review for me!
The web service driver is a Datasource in Cake 2, and a web service driver in Cake 3.
The use-case is a centrally managed data-store
ApiUpdatable has a queue to schedule updates from the client site
It also has a shell run on a cron
Thin clients pull data from the web service on render and use local cache
Multiple versions of the API with clients being migrated over
First ever api I built
A plugin seemed logical at the time
Coded first rather than designing it
Difficult containing deep associations, 4-5 levels
Ended up filtering data in the view, so the pagination meta was wrong
Index method in ApiController is 247 lines long!
If your controller looks like this, stop, and rethink
Not flexible enough to expand to cover new use-cases easily
Plugin needs tagging and releasing
Duplicating functionality which already exists in the application
Manual work to create the views
Who knows what past me was thinking with this one.
Even the templates are in the other plugin!
All base endpoint methods are in the AppController
Overload the methods as needed
Extended methods means filtering would be complex on a per endpoint basis
Never even made it out of the branch!
Reqs:
Allows for complex sorting and filtering combinations
Makes my client implementation simple
Full comparison table data
Pragmatic approach, practice rather than theory
Fulfils use-cases of complex full data returns
Not using the RESTful routing
Hidden the _matchingData
Focus on simplicity for the client
The utopian vision of everything being achievable
Controller inheritance
Crud plugin makes it easy
Custom actions to call behavour methods on the query
Behaviour uses custom finders to modify query
Uses the ORMs stackable finders feature
Doesn't adhere to REST standard, pragmatic and solo! ;)
Filter and contain classes implement an interface, and customised per endpoint
As Jose said in the workshop, don't be scared to go outside the framework
Related data class also deals with sortable whitelist
Luckily I have only 5 endpoints, 2 primary ones
Neat code separation
Can still inherit from ApiController
Easy to run multiple versions from the same app
Can help you build a basic api in literally minutes!
Comes with pagination and sorting out of the box
Has various api listeners,
Api
ApiPaginator
ApiQueryLog
Search listener implements the friendsofcake/search plugin
Not as complicated as it appears, I'm working on the docs, so any contributions welcome!
Copy and paste of the default crud action
Just added some extra methods to hook into the query object
The methods just look for the behaviour and call it's custom finders on the query
Example Crud index action method
_table is the current table, proxied call to Controller::loadModel() in Crud core
Just passing a Cake\ORM\Query object
Awesome because it's lazy I can build the query programatically and execute it later
Relies on awesome Cake 3 ORM with closures in my contains to filter my associations
Highly customisable
Boss asked to remove pricing, comment out the line, job done, 2 mins – comment because I know he'll change his mind ;)
Low impact, as it's tied to a single endpoint
Lots of code though, so can look daunting
Manages both Joins and Contains, as some fields must be joined for filtering and sorting
Lives in src/Lib/Api/RelatedData
Build filters as an array in the url
I can pass all the filters around by just passing the array
Allow for highly complex filtering
Flexible and easy for clients to understand
Has custom field filters too such as minPrice and maxPrice which map to the price field
Lives in src/Lib/Api/Filters
Might not want to expose your schema
Could use a custom JSON view or use Fractal
Can return a complete response for the client implementation
For affiliate marketing comparison tables we need things in an arbitrary order set in the client site CMS
Again,
- Might not want to expose your schema
- Could use a custom JSON view or use Fractal
Routing prefixes good for versioning, as they can be nested! So why not an api prefix, with a v2 prefix and v3 prefix?
Don't be constrained by the framework where it doesn't make sense.
Not all of your code needs to fit into the framework, but check first!
Theoretical paralysis, too much thinking not enough doing.
Be pragmatic.
Create a branch, try a thing, ditch it and start over.
Three branches before sorting worked!
Testing using json fixture files
Postman client for testing your endpoints yourself
Cake 2 ApiUpdatable behaviour swaps the datasource based on if the query has the ‘api’ => true option set in the find conditions using a callback
Data returned as json array
Service info configured in your app.php under Webservices config key
Will return json entities directly from a cake 3 endpoint
If you'd like to discuss anything in my talk, please come see me for a chat.