PHPunconf14: Apigility Einführung

Post on 22-Apr-2015

294 views 0 download

description

Eine Einführung in Apigility, das auf dem Zend Framework 2 basiert und mit dem man schnell mal eben seine API zusammen klicken kann.

Transcript of PHPunconf14: Apigility Einführung

APIGILITYAPIGILITYHeute klicken wir unsere API einfach mal zusammen...

1 / 54

Über Ralf EggertÜber Ralf Eggert● PHP seit 1999● Zend Framework 2● Trainer ● Berater● Autor● Insulaner

2 / 54

[A]

PrologProlog

3 / 54

[1]

Neue APIin 2 Stunden?

4 / 54

[2]

5 / 54

[3]

In a NutshellIn a Nutshell

6 / 54

Restful Web ServicesRestful Web Services

CLIENT

Web Browser

PHP

Javascript

RUBY

PYTHON

REST SERVER

/api/user/123

/api/user

/api/user

/api/user/123

/api/user/123

USER DOMAIN

getUserEntity()

getUserCollection()

addUserEntity()

updateUserEntity()

deleteUserEntity()

GET Request

JSON Response

GET Request

JSON Response

POST Request

JSON Response

PUT Request

JSON Response

DELETE Request

JSON Response

Integer

UserEntity

void

UserCollection

Array

Boolean

Integer, Array

Boolean

Integer

Boolean

7 / 54

RPC Web ServicesRPC Web ServicesLocalCLIENT

javascript

RPCClient

JSON

Method:getUserParams:id

USER DOMAIN

getUserEntity()

GET Request

JSON Result

Integer

UserEntity

RPCServer

/json-rpc.php

Remote Call

JSON Result

javascriptJSON

Method:addUserParams:name

addUserEntity()POST Request

JSON Result

Array

Boolean

/json-rpc.phpRemote Call

JSON Result

javascriptXML

Method:getUserParams:id

getUserEntity()

GET Request

XML Result

Integer

UserEntity

/xml-rpc.php

Remote Call

XML Result

javascriptXML

Method:addUserParams:name

addUserEntity()

POST Request

XML Result

Array

Boolean

/xml-rpc.php

Remote Call

XML Result

8 / 54

VersionierungVersionierung

default Version per URLdefault Version per URL

Version 1 per URLVersion 1 per URL

Version 2 per URLVersion 2 per URL

default Version per Content Negotiationdefault Version per Content Negotiation

Version 1 per Content NegotiationVersion 1 per Content Negotiation

Version 2 per Content NegotiationVersion 2 per Content Negotiation

9 / 54

JSON / HAL / ProblemJSON / HAL / Problem

10 / 54

WEITERE FEATURESWEITERE FEATURES[4, 5, 6, 7, 8, 9]

Datenbank-basiertDatenbank-basiert Code-basiertCode-basiert authentifizierungauthentifizierung

API DokumentationAPI Dokumentation DatenvalidierungDatenvalidierung DeploymentDeployment

11 / 54

ModularisierungModularisierungZf-apigilityZf-apigility Zf-apigility-adminZf-apigility-admin Zf-apigility-documentationZf-apigility-documentation

Zf-apigility-documentation-swaggerZf-apigility-documentation-swagger Zf-apigility-providerZf-apigility-provider Zf-apigility-WelcomeZf-apigility-Welcome

Zf-api-problemZf-api-problem Zf-configurationZf-configuration Zf-content-negotiationZf-content-negotiation

Zf-content-validationZf-content-validation Zf-deployZf-deploy Zf-development-modeZf-development-mode

Zf-halZf-hal Zf-mvc-authZf-mvc-auth Zf-oauth2Zf-oauth2

Zf-restZf-rest Zf-RPCZf-RPC Zf-versioningZf-versioning

12 / 54

InstallationInstallationComposer direkt$ curl -s https://getcomposer.org/installer | php --$ php composer.phar create-project -sdev zfcampus/zf-apigility-skeleton /path/to/install$ cd /path/to/install

ZIP und Composer$ wget https://github.com/zfcampus/zf-apigility-skeleton/archive/master.zip$ unzip -d /path/to/install master.zip $ cd /path/to/install$ php composer.phar install

Git und Composer$ git clone https://github.com/zfcampus/zf-apigility-skeleton.git /path/to/install$ cd /path/to/install$ php composer.phar install

Entwicklungsmodus einschalten und Rechte setzen$ php public/index.php development enable$ sudo chmod 777 -R config/$ sudo chmod 777 -R data/$ sudo chmod 777 -R module/

13 / 54

StartseiteStartseite

14 / 54

[4]

DB-basierterDB-basierterREST-ServiceREST-Service

15 / 54

DatenbankmodellDatenbankmodell

16 / 54

DatenbankadapterDatenbankadapter

Schritte 1 bis 617 / 54

Rest Service, DB-basiertRest Service, DB-basiert

Schritte 1 bis 618 / 54

Rest Service, DB-basiertRest Service, DB-basiert

Schritte 7 bis 1219 / 54

Rest Service, DB-basiertRest Service, DB-basiert

Schritte 13 bis 1820 / 54

REST Service User ListeREST Service User Liste

21 / 54

REST Service User EntityREST Service User Entity

22 / 54

[10]

POSTPOSTPUTPUT

DELETEDELETE23 / 54

Rest Service Post IRest Service Post I

Schritte 1 bis 324 / 54

Rest Service Post IIRest Service Post II

Schritte 4 bis 625 / 54

Rest Service Put IRest Service Put I

Schritte 1 bis 326 / 54

Rest Service Put IIRest Service Put II

Schritt 427 / 54

Rest Service DeleteRest Service Delete

Schritte 1 bis 328 / 54

[6]

AUTHENTIFI-AUTHENTIFI-ZIERUNGZIERUNG

29 / 54

Authentifizierung IAuthentifizierung I

HTpasswd$ htpasswd -c data/htpasswd apigilityNew password: *********Re-type new password: *********Adding password for user apigility$

OAUTH2HTTP DIGESTHTTP BASIC

30 / 54

Authentifizierung IIAuthentifizierung II

Schritte 1 bis 631 / 54

Authentifizierung IIIAuthentifizierung III

Schritt 732 / 54

Authentifizierung IVAuthentifizierung IV

Schritt 833 / 54

[5]

Code-basierterCode-basierterREST SERVICEREST SERVICE

34 / 54

Rest Service, Code-basiertRest Service, Code-basiert

Schritte 1 bis 635 / 54

Rest Service, Code-basiertRest Service, Code-basiert

Schritte 7 bis 1236 / 54

[11]

Was ist daran Code-basiert?

37 / 54

Generierte DateienGenerierte DateienDateirechte setzen und Dateien anzeigen$ cd /home/devhost/apigility.local/$ sudo chmod 777 -R module/User/src/User/V2$ ls -al module/User/src/User/V2/Rest/UserProfile/drwxrwxrwx 2 www-data www-data 4096 Aug 30 21:39 .drwxrwxrwx 4 www-data www-data 4096 Aug 30 21:39 ..-rwxrwxrwx 1 www-data www-data 126 Aug 30 21:39 UserProfileCollection.php-rwxrwxrwx 1 www-data www-data 73 Aug 30 21:39 UserProfileEntity.php-rwxrwxrwx 1 www-data www-data 177 Aug 30 21:39 UserProfileResourceFactory.php-rwxrwxrwx 1 www-data www-data 2341 Aug 30 21:39 UserProfileResource.php

Verzeichnis für table Gateway Klassen anlegen$ cd module/User/src/User/V2/Rest/UserProfile/$ mkdir Table

38 / 54

User Profile entityUser Profile entitynamespace User\V2\Rest\UserProfile;

class UserProfileEntity{ protected $id; protected $name; protected $email; protected $contacts; protected $websites;

public function setId($id) {} public function getId() {}

public function setName($name) {} public function getName() {}

public function setEmail($email) {} public function getEmail() {}

public function setContacts(array $contacts) {} public function getContacts() {}

public function setWebsites(array $websites) {} public function getWebsites() {}}

39 / 54

User Table IUser Table Inamespace User\V2\Rest\UserProfile\Table;

use Zend\Db\Adapter\AdapterInterface;use Zend\Db\ResultSet\ResultSetInterface;use Zend\Db\TableGateway\TableGateway;

class UserTable extends TableGateway{ public function __construct( AdapterInterface $adapter, ResultSetInterface $resultSetPrototype = null ) { $table = 'users';

parent::__construct($table, $adapter, null, $resultSetPrototype); }

public function fetchUserById($id) { $select = $this->getSql()->select(); $select->where->equalTo('id', $id);

return $this->selectWith($select)->current(); }

[...]}

40 / 54

User Table IIUser Table IInamespace User\V2\Rest\UserProfile\Table;

class UserTable extends TableGateway{ [...]

public function fetchContactsById($id) { $select = $this->getSql()->select(); $select->join('user_contacts', 'user_id_2 = id', array()); $select->where->equalTo('user_id_1', $id);

return $this->selectWith($select)->toArray(); }

public function fetchUsers($params) { $select = $this->getSql()->select();

return $this->selectWith($select)->toArray(); }}

41 / 54

User Table factoryUser Table factorynamespace User\V2\Rest\UserProfile\Table;

use Zend\Db\ResultSet\ResultSet;use Zend\ServiceManager\FactoryInterface;use Zend\ServiceManager\ServiceLocatorInterface;

class UserTableFactory implements FactoryInterface{ public function createService(ServiceLocatorInterface $serviceLocator) { $dbAdapter = $serviceLocator->get('MysqlAdapter');

$resultSet = new ResultSet(ResultSet::TYPE_ARRAY);

$table = new UserTable($dbAdapter, $resultSet);

return $table; }}

42 / 54

Website TableWebsite Tablenamespace User\V2\Rest\UserProfile\Table;

use Zend\Db\Adapter\AdapterInterface;use Zend\Db\ResultSet\ResultSetInterface;use Zend\Db\TableGateway\TableGateway;

class WebsiteTable extends TableGateway{ public function __construct( AdapterInterface $adapter, ResultSetInterface $resultSetPrototype = null ) { $table = 'websites';

parent::__construct($table, $adapter, null, $resultSetPrototype); }

public function fetchWebsitesById($id) { $select = $this->getSql()->select(); $select->join('user_websites', 'website_id = id', array()); $select->where->equalTo('user_id', $id);

return $this->selectWith($select)->toArray(); }}

43 / 54

Website FactoryWebsite Factorynamespace User\V2\Rest\UserProfile\Table;

use Zend\Db\ResultSet\ResultSet;use Zend\ServiceManager\FactoryInterface;use Zend\ServiceManager\ServiceLocatorInterface;

class WebsiteTableFactory implements FactoryInterface{ public function createService(ServiceLocatorInterface $serviceLocator) { $dbAdapter = $serviceLocator->get('MysqlAdapter');

$resultSet = new ResultSet(ResultSet::TYPE_ARRAY);

$table = new WebsiteTable($dbAdapter, $resultSet);

return $table; }

}

44 / 54

User Profile Resource IUser Profile Resource Inamespace User\V2\Rest\UserProfile;

use User\V2\Rest\UserProfile\Table\UserTable;use User\V2\Rest\UserProfile\Table\WebsiteTable;use ZF\ApiProblem\ApiProblem;use ZF\Rest\AbstractResourceListener;

class UserProfileResource extends AbstractResourceListener{ protected $userTable; protected $websiteTable;

public function setUserTable($userTable) {} public function getUserTable() {} public function setWebsiteTable($websiteTable) {} public function getWebsiteTable() {}

protected function addContactsAndWebsites(array $user) { $user['contacts'] = $this->getUserTable()->fetchContactsById($user['id']); $user['websites'] = $this->getWebsiteTable()->fetchWebsitesById($user['id']);

return $user; }

[...]}

45 / 54

User Profile Resource IIUser Profile Resource IInamespace User\V2\Rest\UserProfile;

class UserProfileResource extends AbstractResourceListener{ [...]

public function create($data) { return new ApiProblem(405, 'The POST method has not been defined'); }

public function delete($id) {} public function deleteList($data) {}

public function fetch($id) { $user = $this->getUserTable()->fetchUserById($id);

if (!$user) { return new ApiProblem(404, 'User profile for id ' . $id . ' not found'); }

return $this->addContactsAndWebsites($user); }

[...]}

46 / 54

User Profile Resource IIIUser Profile Resource IIInamespace User\V2\Rest\UserProfile;

class UserProfileResource extends AbstractResourceListener{ [...]

public function fetchAll($params = array()) { $users = $this->getUserTable()->fetchUsers($params);

if (!$users) { return new ApiProblem(404, 'No user profiles found'); }

foreach ($users as $key => $user) { $users[$key] = $this->addContactsAndWebsites($user); }

return $users; }

public function patch($id, $data) {} public function replaceList($data) {} public function update($id, $data) {}}

47 / 54

User Profile Resource FactoryUser Profile Resource Factorynamespace User\V2\Rest\UserProfile;

class UserProfileResourceFactory{ public function __invoke($services) { $userTable = $services->get( 'User\\V2\\Rest\\UserProfile\\Table\\UserTable' ); $websiteTable = $services->get( 'User\\V2\\Rest\\UserProfile\\Table\\WebsiteTable' );

$resource = new UserProfileResource(); $resource->setUserTable($userTable); $resource->setWebsiteTable($websiteTable);

return $resource; }}

48 / 54

User Profile Module ConfigUser Profile Module Configreturn array( [...]

'service_manager' => array( 'factories' => array( 'User\\V2\\Rest\\UserProfile\\UserProfileResource' => 'User\\V2\\Rest\\UserProfile\\UserProfileResourceFactory', 'User\\V2\\Rest\\UserProfile\\Table\\UserTable' => 'User\\V2\\Rest\\UserProfile\\Table\\UserTableFactory', 'User\\V2\\Rest\\UserProfile\\Table\\WebsiteTable' => 'User\\V2\\Rest\\UserProfile\\Table\\WebsiteTableFactory', ), ),);

49 / 54

REST Service ProfilREST Service Profil

50 / 54

REST Service ProfilListeREST Service ProfilListe

51 / 54

[A]

EPILOGEPILOG

52 / 54

API ZUSAMMENKLICKENAPI ZUSAMMENKLICKENKlappt für einfache APIs, ansonsten ist Handarbeit erforderlich...

Repository: https://github.com/RalfEggert/phpughh-apigility

53 / 54

BildnachweisBildnachweis[A] Fotos von Ralf Eggert

[1] Still here https://www.flickr.com/photos/thenovys/3784261365 von Abe Novy - CC-BY https://creativecommons.org/licenses/by/2.0/

[2] Young student https://www.flickr.com/photos/audiolucistore/14159712431/ von www.audio-luci-store.it - CC-BY https://creativecommons.org/licenses/by/2.0/

[3] Acorns https://www.flickr.com/photos/dno1967b/5431273344 von Daniel Oines - CC-BY https://creativecommons.org/licenses/by/2.0/

[4] Fixing the database https://www.flickr.com/photos/dahlstroms/4140461901 von Håkan Dahlström - CC-BY https://creativecommons.org/licenses/by/2.0/

[5] Monaco 14pt https://www.flickr.com/photos/polarity/3138680190 von Robert Agthe - CC-BY https://creativecommons.org/licenses/by/2.0/

[6] RSA Securid Token - Credit Card Style https://www.flickr.com/photos/purpleslog/265657780 von Purple Slog - CC-BY https://creativecommons.org/licenses/by/2.0/

[7] Shelf of Used Books https://www.flickr.com/photos/thedarkthing/5363586197 von William Ross - CC-BY https://creativecommons.org/licenses/by/2.0/

[8] Ticket validator at Nice train station https://www.flickr.com/photos/traveleden/3797157077 von Simon - CC-BY https://creativecommons.org/licenses/by/2.0/

[9] Test Lab - Supermicro Storage https://www.flickr.com/photos/jemimus/8533890844 von Robert - CC-BY https://creativecommons.org/licenses/by/2.0/

[10] We are all fan of laptops https://www.flickr.com/photos/scottvanderchijs/4493248747 von Scott & Elaine van der Chijs - CC-BY https://creativecommons.org/licenses/by/2.0/

[11] Surprise https://www.flickr.com/photos/photographybycolby/11927931295 von Colby Stopa - CC-BY https://creativecommons.org/licenses/by/2.0/

Alle weiteren Screenshots wurden von Ralf Eggert erstellt.

54 / 54