Mise en place de l'api Instagram dans une application ZendFramework 1.12 - Zend Framework 1.12 partie 10

Mise en place de l'api Instagram dans une application ZendFramework 1.12

Publié le 02/10/2013

Nous allons mettre en place un login via l'api d'instagram dans une page de notre app ZendFramework 1.12. Cette dernière va appeler des photos de l'utilisateur et les affichera. elle va aussi appeler des photos pour un tag donné et quelques count seront effectué sur le likes et comments pour se faire la main. Dans le tutorial notre applications zf va s'appeler zgchic et vous retrouverez ce préfixe dans les différentes classes présentées. Ce préfixe sera à remplacer par celui défini dans votre application.

Il vous faudra un compte instagram et créer un nouveau client pour notre app zf à l'adresse suivante :

instagram.com/developer/

Il vous sera demandé un nom pour notre app et l'adresse du site web ou elle sera appelée :

http://www.domaine.tld

Et l'adresse de redirection une fois que l'authentification a eu lieu. Ici ce sera la route vers notre contrôleur Instagram que allons créer par la suite :

http://www.domaine.tld/instagram

Une fois le client enregistré, Instagram va nous fournir un client id et client secret. Ces informations vont nous servir pour le login.

1 - Création d'un table pour l'enregistrement des utilisateurs instagram

Dans la bdd de notre app zf nous allons créer une table "instagram_users" qui va nous servir à enregistrer le token utilisateur facilitant sa prochaine connexion.


SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `instagram_users`
-- ----------------------------
DROP TABLE IF EXISTS `instagram_users`;
CREATE TABLE `instagram_users` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `user_id` varchar(255) NOT NULL,
  `token` varchar(255) NOT NULL,
  `created_time` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

2 - Mise en place des classes Modèles avec Doctrine

Nous allons déclarer cette nouvelle table dans notre app en créant deux fichiers de Modèle : l'un va contenir la description de la table et l'autre sera la classe où ranger les méthodes afférentes aux opérations CRUD.

Dans /APP/library/Zgchic/Model/BaseInstragramUsers.php :


<?php
// Connection Component Binding
Doctrine_Manager::getInstance()->bindComponent('Zgchic_Model_InstagramUsers', 'doctrine');

/**
 * Zgchic_Model_BaseInstagramUsers
 * 
 * This class has been auto-generated by the Doctrine ORM Framework
 * 
 * @property integer $id
 * @property string $username
 * @property string $user_id
 * @property string $token
 * 
 * @package    ##PACKAGE##
 * @subpackage ##SUBPACKAGE##
 * @author     ##NAME## <##EMAIL##>
 * @version    SVN: $Id: Builder.php 7490 2010-03-29 19:53:27Z jwage $
 */
abstract class Zgchic_Model_BaseInstagramUsers extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->setTableName('instagram_users');
        $this->hasColumn('id', 'integer', 4, array(
             'type' => 'integer',
             'length' => 4,
             'fixed' => false,
             'unsigned' => false,
             'primary' => true,
             'autoincrement' => true,
             ));
        $this->hasColumn('username', 'string', 255, array(
             'type' => 'string',
             'length' => 255,
             'fixed' => false,
             'unsigned' => false,
             'primary' => false,
             'notnull' => true,
             'autoincrement' => false,
             ));
        $this->hasColumn('user_id', 'string', 255, array(
             'type' => 'string',
             'length' => 255,
             'fixed' => false,
             'unsigned' => false,
             'primary' => false,
             'notnull' => true,
             'autoincrement' => false,
             ));
        $this->hasColumn('token', 'string', 255, array(
             'type' => 'string',
             'length' => 255,
             'fixed' => false,
             'unsigned' => false,
             'primary' => false,
             'notnull' => true,
             'autoincrement' => false,
             ));
        $this->hasColumn('created_time', 'timestamp', null, array(
            'type' => 'timestamp',
            'fixed' => false,
            'unsigned' => false,
            'primary' => false,
            'notnull' => true,
            'autoincrement' => false,
        ));
    }

    public function setUp()
    {
        parent::setUp();
        
    }
}

Dans /APP/library/Zgchic/Model/BaseInstragramUsers.php :


<?php

/**
 * Zgchic_Model_InstagramUsers
 * 
 * This class has been auto-generated by the Doctrine ORM Framework
 * 
 * @package    ##PACKAGE##
 * @subpackage ##SUBPACKAGE##
 * @author     ##NAME## <##EMAIL##>
 * @version    SVN: $Id: Builder.php 7490 2010-03-29 19:53:27Z jwage $
 */
class Zgchic_Model_InstagramUsers extends Zgchic_Model_BaseInstagramUsers
{

}

3 - Création d'une classe Instagram qui va contenir les méthodes de communication avec l'api

Dans /APP/library/Zgchic/Model/Instagram.php, remplacer les valeurs : CLIENT_ID et CLIENT_SECRET par celle de votre app :


<?php

class Zgchic_Model_Instgram
{
    /**
     * Post data by curl
     * @static
     * @param $url
     * @param $auth_code
     * @return mixed
     */
    static function postCurl( $url, $auth_code )
    {
        // init curl
        $ch = curl_init();

        // set-up param curl by post
        curl_setopt( $ch, CURLOPT_URL, "https://api.instagram.com/oauth/access_token" );
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt( $ch, CURLOPT_POST, true );

        // set-up post data
        $data = array
        (
            'client_id' => 'CLIENT_ID',
            'client_secret' => 'CLIENT_SECRET',
            'grant_type' => 'authorization_code',
            'redirect_uri' => $url,
            'code' => $auth_code
        );

        // set-up curl form post data
        curl_setopt( $ch, CURLOPT_POSTFIELDS, $data );

        // execute curl and retrieve answer from instagram
        $output = curl_exec( $ch );

        // close curl connection
        curl_close( $ch );

        // json decode
        return $output = json_decode( $output );
    }

    /**
     * Get data by curl
     * @static
     * @param $url
     * @return mixed
     */
    static function getCurl( $url )
    {
        // init curl
        $ch = curl_init();

        // set-up param curl by post
        curl_setopt( $ch, CURLOPT_URL, $url );
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );

        // execute curl and retrieve answer from instagram
        $output = curl_exec( $ch );

        // close curl connection
        curl_close( $ch );

        return $output = json_decode( $output, true );
    }

    /**
     * Get user id by call api
     * @static
     * @param $username
     * @return string
     */
    static function getUserId( $username )
    {
        $username = strtolower( $username );
        $token = "InsertThatHere";
        $url = "https://api.instagram.com/v1/users/search?q=".$username."&access_token=".$token;
        $get = file_get_contents( $url );
        $json = json_decode( $get );

        foreach( $json->data as $user )
        {
            if( $user->username == $username )
                return $user->id;
        }

        return '00000000'; // return this if nothing is found
    }
}

4 - Mise en place des routes, du contrôleur et de la vue dans le module front de notre app

En premier lieu nous allons créer les routes dans le fichier /APP/application/configs/application.ini, dans la section production :


; Routes - front - login instagram

resources.router.routes.logininstagram.route = /logininstagram
resources.router.routes.logininstagram.defaults.module = default
resources.router.routes.logininstagram.defaults.controller = index
resources.router.routes.logininstagram.defaults.action = logininstagram


; Routes - front - success instagram

resources.router.routes.instagram.route = /instagram
resources.router.routes.instagram.defaults.module = default
resources.router.routes.instagram.defaults.controller = instagram
resources.router.routes.instagram.defaults.action = index

Ensuite nous allons créer notre contrôleur dans le module front de notre app zf /APP/application/modules/default/controllers/InstagramController.php dans lequel on prendra soin de remplacer les valeurs 'www.domain.tld' par celle de votre application :


<?php

class InstagramController extends Zend_Controller_Action
{
    public function init()
    {
        // set timezone
        date_default_timezone_set( 'Europe/Paris' );
    }

    public function indexAction()
    {
        try
        {
            // set layout
            $this->_helper->layout->setLayout( 'master' );

            // test if user already logged


            // auth instagram get code
            $auth_code = $_GET['code'];
            //Zend_Debug::dump($auth_code);

            // retrieve user info
            $user_info = Zgchic_Model_Instgram::postCurl( 'http://www.domain.tld/instagram/', $auth_code );
            //Zend_Debug::dump($user_info);

            // test if answer is correct
            if( isset( $user_info->access_token ) )
            {
                // retrieve access token
                $token = $user_info->access_token;

                // test if user already exist
                if( Doctrine_Core::getTable( 'Zgchic_Model_InstagramUsers' )->findOneByUserId( $user_info->user->id ) === false )
                {
                    // store user in db
                    $user = new Zgchic_Model_InstagramUsers();
                    $user->user_id = $user_info->user->id;
                    $user->username = $user_info->user->username;
                    $user->token = $user_info->access_token;
                    $user->created_time = date( 'Y-m-d H:i:s' );
                    $user->save();
                }

                // retrieve full user's info
                $user_info_full = Zgchic_Model_Instgram::getCurl( 'https://api.instagram.com/v1/users/'.$user_info->user->id.'/?access_token='.$user_info->access_token );
                $this->view->user_info_full = $user_info_full['data'];

                // retrieve recent media from user and send to the view
                $this->view->media = Zgchic_Model_Instgram::getCurl( 'https://api.instagram.com/v1/users/'.$user_info->user->id.'/media/recent/?access_token='.$user_info->access_token );

                // calculate total likes, comments and send to view
                $total_likes = 0;
                $total_comments = 0;
                foreach( $this->view->media['data'] as $media )
                {
                    $total_likes += $media['likes']['count'];
                    $total_comments += $media['comments']['count'];
                }

                $this->view->total_likes = $total_likes;
                $this->view->total_comments = $total_comments;

                // retrieve tag and send to the view
                $this->view->tag = Zgchic_Model_Instgram::getCurl( 'https://api.instagram.com/v1/tags/TAG/media/recent?access_token='.$user_info->access_token );

            }
            else
            {
                header( 'Location:https://api.instagram.com/oauth/authorize/?client_id=CLIENT_ID&redirect_uri=http://www.domain.tld/instagram/&response_type=code&scope=likes+comments' );
            }
        }
        catch(Exception $e)
        {
            echo $e->getMessage();
        }
    }
}

Et enfin nous allons créer notre vue :

Dans /APP/application/modules/default/views/scripts/, créer un dossier instagram. Dans ce dossier, créer un fichier index.phtml et y placer le code suivant :


<div id="global">
    <h1>media</h1>
    <p>total media : <span><? echo $this->user_info_full['counts']['media']; ?></span></p>
    <p>followed by : <span><? echo $this->user_info_full['counts']['followed_by']; ?></span></p>
    <p>follows : <span><? echo $this->user_info_full['counts']['follows']; ?></span></p>
    <p>total likes : <span><? echo $this->total_likes; ?></span></p>
    <p>total comments : <span><? echo $this->total_comments; ?></span></p>
    <div>
        <ul class="thumbnails">
            <?php foreach($this->media['data'] as $media): ?>
            <li class="span3">
                <a href="#" class="thumbnail">
                    <img src="<?php echo $media['images']['thumbnail']['url']; ?>" alt="150x150" data-src="holder.js/150x150">
                </a>
                <p>text : <span><?php echo $media['caption']['text']; ?></span></p>
                <p> comments : <span><?php echo $media['comments']['count']; ?></span></p>
                <h3>tags</h3>
                <?php foreach($media['tags'] as $tag): ?>
                <span><?php echo $tag; ?></span>
                <?php endforeach; ?>
                <p>likes : <span><?php echo $media['likes']['count']; ?></span></p>
            </li>
            <?php endforeach; ?>
        </ul>
    </div>
    <h1>tag</h1>
    <div>
        <ul class="thumbnails">
            <?php foreach($this->tag['data'] as $tag_media): ?>
            <li class="span3">
                <a href="<?php echo $tag_media['link']; ?>" class="thumbnail">
                    <img src="<?php echo $tag_media['images']['thumbnail']['url']; ?>" alt="150x150" data-src="holder.js/150x150"/>
                </a>
            </li>
            <?php endforeach; ?>
        </ul>
    </div>
</div>

On va pouvoir maintenant voir le résultat en testant notre travail.

Pour une intégration digne de ce nom il faut mettre un système de cache avec une durée de vie longue si ce module se retrouve utilisé sur la home d'un site.

Liens utiles :

instagram.com/developer/api-console apigee.com/console/instagram

Exemple url autorisation :

https://instagram.com/oauth/authorize/?client_id=CLIENT_ID&redirect_uri=http://www.domain.tld/authinstagram/&response_type=token http://www.domain.tld/index/authinstagram/#access_token=TOKEN