Yii Framework Forum: ActiveResource for Yii - Yii Framework Forum

Jump to content

  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

ActiveResource for Yii ActiveResource for using REST resources as models Rate Topic: ***** 3 Votes

#1 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 04 July 2011 - 02:27 PM

*
POPULAR

Hi folks,

I just pushed an alpha version of "ActiveResource for Yii" to Github (https://github.com/H.../ActiveResource)

What is Active Resource?
The goal is to use REST services and their resources as if they were schemaless ActiveRecord models. I was using a lot of different REST services these days (Neo4j for example is a graph database exposed as RESTful service) and soon recognized that a lot of them could need the same features an CActiveRecord model would provide.
So instead of using a database as persistent storage this class uses a REST service. This project is inspired by the Rails version of ActiveResource (http://api.rubyonrai...ource/Base.html)

How do you I use it?
Please follow the instructions on GitHub. It should be really straight forward.

Give me an example so I can see if this could be interesting for me:
Imagine a webservice exposing "people" resources via REST. A simple EActiveResource model could look like this:

<?php
class Person extends EActiveResource
{

    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function rest()
    {
        return array(
            'site'=>'http://api.aRESTservice.com',
            'resource'=>'people',
            'contenttype'=>'application/json',
            'accepttype'=>'application/json',
            'fileextension'=>'.json', //Twitter always wants you to append this to your GET requests for example
        );
    }
    
}
?>


Then you want to add a new Person
$person=new Person;
$person->setAttributes(array(
	'name'=>'Haensel',
	'gender'=>'m'
));
$person->save(); //validation fails, no POST request is sent to the service. You can get the error messages like you would with CActiveRecord

$person=new Person;
$person->setAttributes(array(
	'name'=>'Haensel',
	'gender'=>1
));
$person->save(); // VALIDATED. Sending POST request to http://api.aRESTservice.com/people with data '{'name':'Haensel','gender':1}'


Ok, so why isn't this an extension already?
This is an early version and I don't want you to be mad at me when it sucks. For example basic authentication isn't fully implemented yet although that should be easy. OAuth for example could be a bit more tricky. And it isn't possible to send XML, you are only able to RECEIVE XML and JSON or send JSON or urlencoded data.
If the release becomes stable I'll sure add it to the extensions.



So what do you think? Nice, bad? Anything is welcome and if you find bugs or general problems you can post it to my GitHub repository. This should be the HQ. If you have any additional question drop me a line on twitter (@Haensel).
I also used extensive tracing, so activate tracing and look for "ext.EActiveResource" to find possible problems.

Cheers,

Haensel
5

#2 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 05 July 2011 - 02:14 AM

Even though i didn't have to deal with REST yet, this seems to be pretty useful. I also like the simple syntax that resembles AR. Maybe i would not call the configuration method "Configuration" which doesn't look very Yii'ish :) How about "rest()" or something, similar to AR's table() method?

Apart from that i wonder, if you can also extend it with find*() operations. But this probably requires a standard query schema for REST - which probably doesn't exist, right?
0

#3 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 05 July 2011 - 03:01 AM

Hi Mike,

View PostMike, on 05 July 2011 - 02:14 AM, said:

Even though i didn't have to deal with REST yet, this seems to be pretty useful. I also like the simple syntax that resembles AR. Maybe i would not call the configuration method "Configuration" which doesn't look very Yii'ish :) How about "rest()" or something, similar to AR's table() method?


You are right, maybe I'll rename this :)

View PostMike, on 05 July 2011 - 02:14 AM, said:

Apart from that i wonder, if you can also extend it with find*() operations. But this probably requires a standard query schema for REST - which probably doesn't exist, right?

It should be easily extensible, but as there is no "fits all" way of doing it I desided to just implement the very basic AR functions (like findById(), updateById(),deleteById()). In case of find() some services would require a GET request including parameters like "?name=Test&gender=1", some would need a prefix like "q" "?q=name:test&gender:1", others even need a POST request with some complex query in the body of the request (although that's not a very RESTful approach).
Just to give you an example:

Twitter exposes statuses and users etc. via REST (although not very RESTful, meaning no standard schema is used), so one would have to play around with it a bit. I want statuses and statuses always return an embedded user object, so one could write two classes

First, let's specify a Tweet model

<?php

class Tweet extends EActiveResource
{

    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function rest()
    {
        return array(
            'site'=>'http://api.twitter.com/1',
            'resource'=>'statuses',
            'contenttype'=>'application/json',
            'accepttype'=>'application/xml',
            'container'=>'status',
            'fileextension'=>'.xml',
            'embedded'=>array(
                'user'=>array(self::IS_ONE,'TwitterUser')
            ),

        );
    }

    public function findById($id)
    {
        return $this->populateRecord($this->getRequest('show/'.$id));
    }

    public function publicTimeline()
    {
        return self::model()->populateRecords(self::model()->getRequest('public_timeline'));
    }
    
}

?>

A tweet always contains a user, we specified that with HAS_ONE

<?php

class TwitterUser extends EActiveResource
{

    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function rest()
    {
        return array(
            'site'=>'http://api.twitter.com/1',
            'resource'=>'users',
            'idProperty'=>'id',
            'container'=>'user',
            'contenttype'=>'application/json',
            'accepttype'=>'application/xml',
            'embedded'=>array(
                'status'=>array(self::IS_ONE,'Tweet')
            ),
            'fileextension'=>'.xml',
        );
    }

    public function findById($id) {
        $response=$this->getRequest('show/'.$id);
        return $this->populateRecord($response);
    }

}


<php
$publicTweets=Tweet::model()->publicTimeline();
$userOfFirstTweet=$publicTweets[0]->user; //the embedded user object
$username=$userOfFirstTweet->name;
//now let's try to only get the user object without the tweet
$user=TwitterUser::model()->findById($userOfFirstTweet->id);

?>

1

#4 User is online   Rodrigo Coelho 

  • Master Member
  • PipPipPipPip
  • Yii
  • Group: Members
  • Posts: 664
  • Joined: 05-August 10
  • Location:Rio de Janeiro, Brazil

Posted 05 July 2011 - 03:35 PM

This is beautiful.
0

#5 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 06 July 2011 - 08:41 AM

Thanks,

Just added it to the extensions (http://www.yiiframew...activeresource/) because I think people will only use it if it is mentioned in extensions.
You can find a small wiki on http://github.com/Ha...ActiveResource/ and I changed the Configuration() method to rest() as Mike mentioned above.
I hope it will be of use for some people.

greetings,

Haensel
1

#6 User is offline   jacmoe 

  • Elite Member
  • Yii
  • Group: Moderators
  • Posts: 2,601
  • Joined: 10-October 10
  • Location:Denmark

Posted 06 July 2011 - 09:06 AM

View PostHaensel, on 06 July 2011 - 08:41 AM, said:

Thanks,

Just added it to the extensions (http://www.yiiframew...activeresource/) because I think people will only use it if it is mentioned in extensions.

Yup, you're right.
How are people supposed to know it exists if it's not there?
Anyway, great work! :lol:
"Less noise - more signal"
0

#7 User is offline   robregonm 

  • Expert Yii Developer
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 594
  • Joined: 30-July 09
  • Location:Colombia

Posted 06 July 2011 - 09:07 AM

This extension looks great. I'll give it a try, I was needing something like this some months ago.

Great job.

Regards,

Ricardo
Ricardo Obregón

YiiFramework en Español - http://yiiframework.co/ - http://yiiframeworkenespanol.org/ - Yii Code Generator for Bootstrap
http://obregon.co/ - https://1server.co/
PHP 5.5+, nginx 1.7, MySQL(MariaDB & PerconaDB), PostgreSQL 9, Yii 2, CanJS
Follow me: @robregonm & @obregonco & @1ServerCo.
0

#8 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 12 July 2011 - 04:23 AM

For those interested:

ActiveResource v0.2 is out now:
Better error handling (tracing of error responses returned by the service) and custom exceptions for common http errors (400-407 and 500)
0

#9 User is offline   yiqing95 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 586
  • Joined: 27-December 10
  • Location:china

Posted 21 October 2011 - 09:09 AM

great job , :lol: it ' s useful for develop SOA website .
0

#10 User is offline   markscarbrough 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 6
  • Joined: 20-October 11

Posted 25 October 2011 - 04:57 PM

This is a great idea. I just developed an extension that uses REST extensively and I wished I had seen this first!
It's better to know whether there will be weather, than what the weather will be.
0

#11 User is offline   WouterN 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 28
  • Joined: 06-October 11

Posted 04 December 2011 - 02:06 AM

Hi Haensel (we've had contact via Twitter about this), is this used somewhere in production modus? Would be very interested to see this.. Unfortunately my project (for which I might need this extension) got delayed but the project is still very much alive.. Looking forward to using this and improving the Extension.
0

#12 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 11 December 2011 - 03:43 AM

Hi guys,

@Wouter: Yeah, I remember. Regarding your question: No, it isn't used in production yet but I am working on a project that should be out at the end of January (hopefully). EActiveResource is used for my entire database layer. I use Neo4j - a graph database and I also developed an object oriented interface on top of active resource to interact with it and it seems quite stable so far but there are still some things that will change which could cause problems later on.

1. Being able to define a schema. It can be very helpful to be completely schemaless but it can also be important to define the datatype of certain attributes. If using EActiveResource in combination with CActiveForm every attribute will always be treated as string (because the $_POST/$_GET arrays deliver them as such). By defining an attribute as float or integer etc. in a config array this could be solved by explicitly casting the datatypes before saving/sending them to to service.

2. OAuth and stuff: I never use authentication so I never used OAuth and don't have any experience with it but I know that this is a very important part. Allowing custom headers for requests seems to be a first step of many to come.

3. Tests: Probably the most crucial point. The best way would be to create a yii application with an rest api and do the CRUD stuff with active resource but this is a lot of work and I didn't have the chance to take care of that as of now.

As always. If anyone wants to be part of this project she/he would be very welcome. I suck at Github so there may be some complications regarding push requests and stuff, but I am sure I'll figure that out some day. Contributions are welcome ;)

Thanks and greetings,

Hannes

P.S.: If you have something that really bugs you it is probably best to use Github to report it or drop me a line via email/Twitter (@Haensel)/G+ (https://plus.google....729155174852177) as I can't check all the different channels out there frequently enough. I totally forgot about this thread for a while
0

#13 User is offline   Antonio Ramirez 

  • Elite Member
  • Yii
  • Group: Yii Dev Team
  • Posts: 1,448
  • Joined: 04-October 10

Posted 11 December 2011 - 02:13 PM

Very good job Haensel.
¿How long would it take for you to understand that you own nothing in this world?

www.ramirezcobos.com
www.2amigos.us
www.github.com/tonydspaniard
www.github.com/2amigos


Posted Image
0

#14 User is offline   WouterN 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 28
  • Joined: 06-October 11

Posted 12 December 2011 - 02:23 PM

View Postmarkscarbrough, on 25 October 2011 - 04:57 PM, said:

This is a great idea. I just developed an extension that uses REST extensively and I wished I had seen this first!


Would you be interested in sharing your extension? :)
0

#15 User is offline   WouterN 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 28
  • Joined: 06-October 11

Posted 13 December 2011 - 02:59 AM

View PostHaensel, on 11 December 2011 - 03:43 AM, said:

Hi guys,

@Wouter: Yeah, I remember. Regarding your question: No, it isn't used in production yet but I am working on a project that should be out at the end of January (hopefully). EActiveResource is used for my entire database layer. I use Neo4j - a graph database and I also developed an object oriented interface on top of active resource to interact with it and it seems quite stable so far but there are still some things that will change which could cause problems later on.

1. Being able to define a schema. It can be very helpful to be completely schemaless but it can also be important to define the datatype of certain attributes. If using EActiveResource in combination with CActiveForm every attribute will always be treated as string (because the $_POST/$_GET arrays deliver them as such). By defining an attribute as float or integer etc. in a config array this could be solved by explicitly casting the datatypes before saving/sending them to to service.

2. OAuth and stuff: I never use authentication so I never used OAuth and don't have any experience with it but I know that this is a very important part. Allowing custom headers for requests seems to be a first step of many to come.

3. Tests: Probably the most crucial point. The best way would be to create a yii application with an rest api and do the CRUD stuff with active resource but this is a lot of work and I didn't have the chance to take care of that as of now.

As always. If anyone wants to be part of this project she/he would be very welcome. I suck at Github so there may be some complications regarding push requests and stuff, but I am sure I'll figure that out some day. Contributions are welcome ;)

Thanks and greetings,

Hannes

P.S.: If you have something that really bugs you it is probably best to use Github to report it or drop me a line via email/Twitter (@Haensel)/G+ (https://plus.google....729155174852177) as I can't check all the different channels out there frequently enough. I totally forgot about this thread for a while


Hi Hannes, thanks for your extensive reply :)

Our situation is as follows: we have a REST API (with authentication based on an APIKEY/SECRET that creates a token with which one can perform API calls) that enables us to get, post, put and delete all the objects that we need in our app(s). On top of that API we are going to build our Yii applications for which we would use this extension. Currently our biggest application runs directly on the database and the rest of our applications are already using the API. However, they are not build in Yii :(

Our project consists of two parts: first we are going to extend our REST API with some API calls and JSON output (currently only XML output) and replace the full top layer to run on the Yii framework and connect to the API. So no more direct database connections! After that we are going to replace the API with something that also runs on the Yii framework. Everything is going to run on AWS for maximum scalability. We will use Elasticache as our caching mechanism together with S3 to store the themes of our customers (each customer has a bucket for static + theme/template files).

1) This would be very helpful and of course something that would further streamline the way we develop our applications.

2) Yes, we are planning to implement an oAuth provider as well on our API. This will be one of the last parts of the first half of our project.

3) TDD is something that has hi priority for me. I guess we can work around it and create our own PHPUnit tests or we will add something to the extension to make this work in a nicer way..

Thanks!
0

#16 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 19 December 2011 - 12:55 PM

Hi guys,

I updated to V0.5 now. See the extension page for more info. I basically added support for defining a schema with the benefit of having all magic methods working as expected now (same as with CActiveRecord). Example:

class Person extends EActiveResource
     {
     /* The id that uniquely identifies a person. This attribute is not defined
      * as a property      
      * because we don't want to send it back to the service like a name, surname or    
      * gender etc.
      */
     public $id;

     public static function model($className=__CLASS__)
     {
         return parent::model($className);
     }

     /* Let's define some properties and their datatypes
     public function properties()
     {
         return array(
             'name'=>array('type'=>'string'),
             'surname'=>array('type'=>'string'),
             'gender'=>array('type'=>'string'),
             'age'=>array('type'=>'integer'),
             'married'=>array('type'=>'boolean'),
             'salary'=>array('type'=>'double'),
         );
     }

     /* Define rules as usual */
     public function rules()
     {
         return array(
             array('name,surname,gender,age,married,salary','safe'),
             array('age','numerical','integerOnly'=>true),
             array('married','boolean'),
             array('salary','numerical')
         );
     }

     /* Add some custom labels for forms etc. */
     public function attributeLabels()
     {
         return array(
             'name'=>'First name',
             'surname'=>'Last name',
             'salary'=>'Your monthly salary',
         );
     }
 }


/* GET to http://api.example.com/person/1 and populates a single Person model*/
    $person=Person::model()->findById(1);

    /* GET to http://api.example.com/person and populating Person models */
    $persons=Person::model()->findAll();

    /* create a resource
    $person=new Person;
    $person->name='A name';
    $person->age=21;
    $person->save(); POST request. Returns false if the model doesn't validate

    /* Updating a resource (sending a PUT request)
    $person=Person::model()->findById(1);
    $person->name='Another name';
    $person->save(); //PUT request. Returns false if the model doesn't validate

    //or short version
    Person::model()->updateById(1,array('name'=>'Another name'));

    /* DELETE a resource
    $person=Person::model()->findById(1);
    $person->destroy(); //DELETE to http://api.example.com/person/1

    //or short version
    Person::model()->deleteById(1);

    //setting attributes
    $person->attributes=$_POST['Person'];
    if($person->save())
        echo 'yipiie'; //model validated and was saved/updated

2

#17 User is offline   Coksnuss 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 140
  • Joined: 14-May 09

Posted 19 December 2011 - 01:32 PM

I guess this is perfectly what I need for a project of mine where the database shouldn't be used directly by the application but rather be used through a RESTful API in order to make it possible to switch database (from a relational scheme to a "NoSQL" one) if needed.

I'll try if your Extension is suitable for this - Thanks for your effort.
0

#18 User is offline   WouterN 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 28
  • Joined: 06-October 11

Posted 20 December 2011 - 08:44 AM

The next step would be to integrate API authentication (oAuth?). I have the extension running with the Twitter API and will soon connect it to our own API. Love this extension!
0

#19 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 31 December 2011 - 10:49 AM

Hi,

Version 0.6 is here. Next to some bugfixes these are the new features.

  • Added http auth (basic and digest)
  • Added ssl support
  • Added url routes for more flexibility
  • Added support for adding url params


Find more info at: http://www.yiiframew...activeresource/

@WouterN
The OAuth part is trickier than I thought. I think ActiveResource will be (in most cases) used as part of a system (e.g frontend) interacting with another system (e.g backend) without a user having to grant access. To "speak in OAuth": In such a case the resource owner is the system itself. As far as I understand it 2-legged OAuth should make this possible (without the redirects and stuff) but I found it quite hard to actually find good information on this topic.
1

#20 User is offline   nraj 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 3
  • Joined: 16-November 11

Posted 27 January 2012 - 04:55 AM

Hello,

I am trying to revamp my application by associating it with restful APIs instead of ActiveRecord. This extension would be a great help. However,pls. help me understand where should I define the eactiveresource component/connection like so -
class'=>'EActiveResourceConnection',
'site'=>'...',

Thanks
Neeti
0

Share this topic:


  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users