Starship / RestfullYii ¶
- How it works
- Requirements
- Additional Documentation Can be found Here: Git Repo
- Installation
- Controller Setup
- Making Requests
- Customization & Configuration
- Defining Custom Routes
- Testing
- Contributors
- Resources
- License
Makes quickly adding a RESTFul API to your Yii project easy. RestfullYii provides full HTTP verb support (GET, PUT, POST, DELETE) for your resources, as well as the ability to offset, limit, sort, filter, etc… . You will also have the ability to read and manipulate related data with ease.
RestfullYii has been lovingly rebuilt from the metal and is now 100% test covered! The new event based architecture allows for clean and unlimited customization.
How it works ¶
RestfullYii adds a new set of RESTFul routes to your standard routes, but prepends '/api' .
So if you apply RestfullYii to the 'WorkController' you will get the following new routes by default.
[GET] http://yoursite.com/api/work (returns all works)
[GET] http://yoursite.com/api/work/1 (returns work with PK=1)
[POST] http://yoursite.com/api/work (create new work)
[PUT] http://yoursite.com/api/work/1 (update work with PK=1)
[DELETE] http://yoursite.com/api/work/1 (delete work with PK=1)
Requirements ¶
- PHP 5.4.0 (or later)*
- YiiFramework 1.1.14 (or later)
- PHPUnit 3.7 (or later) to run tests.
For older versions of PHP (< 5.4) checkout v1.15
Additional Documentation Can be found Here: Git Repo ¶
Installation ¶
Download and place the 'starship' directory in your Yii extension directory.
In config/main.php you will need to add the RestfullYii alias. This allows for flexability in where you place the extension.
'aliases' => array(
.. .
'RestfullYii' =>realpath(__DIR__ . '/../extensions/starship/RestfullYii'),
.. .
),
`3. Include ext.starship.RestfullYii.config.routes in your main config (see below) or copy the routes and paste them in your components->urlManager->rules in same config.
'components'=>array(
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>require(
dirname(__FILE__).'/../extensions/starship/restfullyii/config/routes.php'
),
),
)
Controller Setup ¶
Adding a set of RESTFul actions to a controller.
- Add the ERestFilter to your controllers filter method.
public function filters()
{
return array(
'accessControl', // perform access control for CRUD operations
array(
'ext.starship.RestfullYii.filters.ERestFilter +
REST.GET, REST.PUT, REST.POST, REST.DELETE'
),
);
}
- Add the ERestActionProvider to your controllers actions method.
public function actions()
{
return array(
'REST.'=>'ext.starship.RestfullYii.actions.ERestActionProvider',
);
}
- If you are using the accessControl filter you need to make sure that access is allowed on all RESTFul routes.
public function accessRules()
{
return array(
array('allow', 'actions'=>array('REST.GET', 'REST.PUT', 'REST.POST', 'REST.DELETE'),
'users'=>array('*'),
),
array('deny', // deny all users
'users'=>array('*'),
),
);
}
Making Requests ¶
To understand how to make RestfullYii API requests its best to look at a few examples. Code examples will be shown first in JavaScript as an AJAX user and then using CURL.
* JS examples use jQuery
* Default validation for an AJAX user is !Yii::app()->user->isGuest so the user must be logged in for this type of request.
GET Requests ¶
Getting a list or resources (WorkController) ¶
JavaScript: ~~~ [javascript] $.ajax({
url:'/api/work',
type:"GET",
success:function(data) {
console.log(data);
},
error:function (xhr, ajaxOptions, thrownError){
console.log(xhr.responseText);
}
}); ~~~
CURL: ~~~ [shell] curl -i -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\ http://my-site.com/api/work ~~~
Response: ~~~ [javascript] {
"success":true,
"message":"Record(s) Found",
"data":{
"totalCount":"30",
"work":[
{
"id": "1",
"title": "title1",
"author_id": "1",
"content": "content1",
"create_time": "2013-08-07 10:09:41"
},
{
"id": "2",
"title": "title2",
"author_id": "2",
"content": "content2",
"create_time": "2013-08-08 11:01:11"
},
. . .,
]
}
} ~~~
Getting a single resource (WorkController) ¶
JavaScript: ~~~ [javascript] $.ajax({
url:'/api/work/1',
type:"GET",
success:function(data) {
console.log(data);
},
error:function (xhr, ajaxOptions, thrownError){
console.log(xhr.responseText);
}
}); ~~~
CURL: ~~~ [shell] curl -i -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\ http://my-site.com/api/work/1 ~~~
Response: ~~~ [javascript] {
"success":true,
"message":"Record Found",
"data":{
"totalCount":"1",
"work":[
{
"id": "1",
"title": "title1",
"author_id": "1",
"content": "content1",
"create_time": "2013-08-07 10:09:41"
}
]
}
} ~~~
GET Request: Limit & Offset (WorkController) ¶
You can limit and paginate through your results by adding the limit and offset variables to the request query string.
JavaScript: ~~~ [javascript] $.ajax({
url:'/api/work?limit=10&offset=30',
type:"GET",
success:function(data) {
console.log(data);
},
error:function (xhr, ajaxOptions, thrownError){
console.log(xhr.responseText);
}
}); ~~~
CURL: ~~~ [shell] curl -i -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\ http://my-site.com/api/work?limit=10&offset=30 ~~~
Response: ~~~ [javascript] {
"success":true,
"message":"Record(s) Found",
"data":{
"totalCount":"30",
"work":[
{
"id": "11",
"title": "title11",
"author_id": "11",
"content": "content11",
"create_time": "2013-08-11 11:10:09"
},
{
"id": "12",
"title": "title12",
"author_id": "12",
"content": "content12",
"create_time": "2013-08-08 12:11:10"
},
. . .,
]
}
}
### GET Request: Sorting results (WorkController)
You can sort your results by any valid param or multiple params as well as provide a sort direction (ASC or DESC). sort=[{"property":"title", "direction":"DESC"}, {"property":"create_time", "direction":"ASC"}]
JavaScript:
[javascript] $.ajax({
url:'/api/work?sort=[{"property":"title", "direction":"DESC"}, {"property":"create_time", "direction":"ASC"}]',
type:"GET",
success:function(data) {
console.log(data);
},
error:function (xhr, ajaxOptions, thrownError){
console.log(xhr.responseText);
}
}); ~~~
CURL: ~~~ [shell] curl -i -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\ http://my-site.com/api/work?sort=[{"property":"title", "direction":"DESC"}, {"property":"create_time", "direction":"ASC"}] ~~~
Response: ~~~ [javascript] {
"success":true,
"message":"Record(s) Found",
"data":{
"totalCount":"30",
"work":[
{
"id": "29",
"title": "title30b",
"author_id": "29",
"content": "content30b",
"create_time": "2013-08-07 14:05:01"
},
{
"id": "30",
"title": "title30",
"author_id": "30",
"content": "content30",
"create_time": "2013-08-08 09:10:09"
},
{
"id": "28",
"title": "title28",
"author_id": "28",
"content": "content28",
"create_time": "2013-08-09 14:05:01"
},
. . .,
]
}
}
### GET Request: Filtering results (WorkController)
You can filter your results by any valid param or multiple params as well as an operator.
Available filter operators:
* in
* not in
* =
* !=
* >
* >=
* <
* <=
* No operator is "LIKE"
/api/post/?filter = [ {"property": "id", "value" : 50, "operator": ">="} , {"property": "user_id", "value" : [1, 5, 10, 14], "operator": "in"} , {"property": "state", "value" : ["save", "deleted"], "operator": "not in"} , {"property": "date", "value" : "2013-01-01", "operator": ">="} , {"property": "date", "value" : "2013-01-31", "operator": "<="} , {"property": "type", "value" : 2, "operator": "!="} ] ~~~
POST Requests (Creating new resources) ¶
With POST requests we must include the resource data as a JSON object in the request body.
JavaScript: ~~~ [javascript] var postData = {
"title": "title31",
"author_id": "31",
"content": "content31",
"create_time": "2013-08-20 09:23:14"
};
$.ajax({
url:'/api/work',
data:JSON.stringify(postData)
type:"POST",
success:function(data) {
console.log(data);
},
error:function (xhr, ajaxOptions, thrownError){
console.log(xhr.responseText);
}
}); ~~~
CURL: ~~~ [shell] curl -l -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\ -X POST -d '{"title": "title31", "author_id": "31", "content": "content31", "create_time": "2013-08-20 09:23:14"}' http://my-site.com/api/work ~~~
Response: ~~~ [javascript] {
"success":true,
"message":"Record Created",
"data":{
"totalCount":"1",
"work":[
{
"id": "31",
"title": "title31",
"author_id": "31",
"content": "content31",
"create_time": "2013-08-20 09:23:14"
}
]
}
} ~~~
PUT Requests (Updating existing resources) ¶
With PUT requests like POST requests we must include the resource data as a JSON object in the request body.
JavaScript: ~~~ [javascript] var postData = {
"id": "31",
"title": "title31",
"author_id": "31",
"content": "content31",
"create_time": "2013-08-20 09:23:14"
};
$.ajax({
url:'/api/work/31',
data:JSON.stringify(postData)
type:"PUT",
success:function(data) {
console.log(data);
},
error:function (xhr, ajaxOptions, thrownError){
console.log(xhr.responseText);
}
}); ~~~
CURL: ~~~ [shell] curl -l -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\ -X PUT -d '{"id": "31", "title": "title31", "author_id": "31", "content": "content31", "create_time": "2013-08-20 09:23:14"}' http://my-site.com/api/work/31 ~~~
Response: ~~~ [javascript] {
"success":true,
"message":"Record Updated",
"data":{
"totalCount":"1",
"work":[
{
"id": "31",
"title": "title31",
"author_id": "31",
"content": "content31",
"create_time": "2013-08-20 09:23:14"
}
]
}
} ~~~
DELETE Requests (Delete a resource) ¶
JavaScript: ~~~ [javascript] $.ajax({
url:'/api/work/1',
type:"DELETE",
success:function(data) {
console.log(data);
},
error:function (xhr, ajaxOptions, thrownError){
console.log(xhr.responseText);
}
}); ~~~
CURL: ~~~ [shell] curl -l -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\ "X-HTTP-Method-Override: DELETE" -X DELETE http://my-site.com/api/work/1
Response:
[javascript] {
"success":true,
"message":"Record Deleted",
"data":{
"totalCount":"1",
"work":[
{
"id": "1",
"title": "title1",
"author_id": "1",
"content": "content1",
"create_time": "2013-08-07 10:09:41"
}
]
}
} ~~~
Sub-Resources ¶
When working with 'many to many' relations you now have the ability to treat them as sub-resources.
Consider:
~~~
URL Format: http://mysite.com/api/<controller>/<id>/<many_many_relation>/<many_many_relation_id>
Getting player 3 who is on team 1
or simply checking whether player 3 is on that team (200 vs. 404)
GET /api/team/1/players/3
getting player 3 who is also on team 3
GET /api/team/3/players/3
Adding player 3 also to team 2
PUT /api/team/2/players/3
Getting all teams of player 3
GET /api/player/3/teams
Remove player 3 from team 1 (Injury) DELETE /api/team/1/players/3
Team 1 found a replacement, who is not registered in league yet
POST /api/player
From payload you get back the id, now place it officially to team 1
PUT /api/team/1/players/44
~~~
Customization & Configuration ¶
RestfullYii's default behaviors can be easily customized though the built-in event system. Almost all aspects of RestFullYii's request / response handling trigger events. Changing RestfullYii's behaviors is as simple as registering the appropriate event handlers. Event handlers can be registered both globally (in the main config) and locally (at the controller level).
To understand how to do this, lets create a scenario that requires some customization and see how we might accomplish it.
Lets say we have two controllers in our API, WorkController and CategoryController. We would like our API to function in the following ways:
- The API should be accessible to JavaScript via AJAX.
- The API should not be accessible to any external client.
- Only registered users should be allowed to view Work and Category resources.
- Only users with the permission REST-UPDATE should be allowed to update works.
- Only users with the permission REST-CREATE should be allowed to create works.
- Only users with the permission REST-DELETE should be allowed to delete works.
- Create, update and delete on categories should be disallowed for all API users.
Now that we know how we would like our API to function, lets take a look at the list above and determine which features can be implemented globally. Since 1, 2 and 3 effect both of our controllers, we can effect these globally by registering a few callbacks in our config/main.php.
To accomplish 1 & 3 we don't have to do anything as this is RestfullYii's default behavior, so that leaves 2. By default RestfullYii does allow access to external clients which is not what we want. Lets change it!
In the /protected/config/main.php params section we will add the following:
$config = array(
..,
'params'=>[
'RestfullYii' => [
'req.auth.user'=>function($application_id, $username, $password) {
return false;
},
]
]
);
This tells RestfullYii that when the event 'req.auth.user' is handled it should always return false. Returning false will deny access (true grants it). Similarly validating an AJAX user has it's own event 'req.auth.ajax.user' which (as mentioned earlier) allows access to registered users by default.
That takes care of our global config and we can now focus on features 4-7 while taking care not to break feature 3. Since features 4-6 involve the work controller and user permissions, we can accomplish all of those at the same time. Remember RestfullYii's default behavior is to allow all registered users complete API access and again this is not what we want. Lets change it!
We will now register an event handler locally in the WorkController; To do this we will need to add a special public method in the WorkController called 'restEvents'. Then once we have the 'restEvents' method we can use one other special method 'onRest' to register our event handler. We can call 'onRest' using '$this->onRest()'. 'onRest' takes two params the first is the event name and the second is a Callable that will actually handle the event. This Callable is bound to the controller so the $this context inside the Callable always refers to the current controller. This is true for event handlers registered both globally as well as locally.
Now we are ready modify the output of event handler 'req.auth.ajax.user', but this time instead of overriding the default behavior, we will use the post-filter feature to add our additional user validation. The event name of a post-filter event is always the main event name prefixed with 'post.filter.', thus in our case the event name is 'post.filter.req.auth.ajax.user'. The param(s) passed into a post-filter handler are always the value(s) returned from the main event. Take a look:
class WorkController extends Controller
{
.. .
public function restEvents()
{
$this->onRest('post.filter.req.auth.ajax.user', function($validation) {
if(!$validation) {
return false;
}
switch ($this->getAction()->getId()) {
case 'REST.POST':
return Yii::app()->user->checkAccess('REST-CREATE');
break;
case 'REST.POST':
return Yii::app()->user->checkAccess('REST-UPDATE');
break;
case 'REST.DELETE':
return Yii::app()->user->checkAccess('REST-DELETE');
break;
default:
return false;
break;
}
});
}
.. .
}
Cool! That just leaves feature 7, disallowing create, update, delete on category. Again we will add this change locally, but this time to the CategoryController. Take a look:
class CategoryController extends Controller
{
.. .
public function restEvents()
{
$this->onRest('post.filter.req.auth.ajax.user', function($validation) {
if(!$validation) {
return false;
}
return ($this->getAction()->getId() == 'REST.GET');
});
}
.. .
}
We now have all features implemented!
Defining Custom Routes ¶
Custom routes are very simple to define as all you really need to do is create an event handler for your route and http verb combination (event name = 'req.
Here is the list of routes we would like to add to our api:
[GET] /api/category/active
[GET] /api/work/special/
[PUT] /api/work/special/
[POST] /api/work/special/
[DELETE] /api/work/special/
Custom Route 1 ¶
As you tell from the route the request will be handled by the Category controller. So that is where we will add our event handler.
class CategoryController extends Controller
{
.. .
public function restEvents()
{
$this->onRest('req.get.active.render', function() {
//Custom logic for this route.
//Should output results.
$this->emitRest('req.render.json', [
[
'type'=>'raw',
'data'=>['active'=>true]
]
])
});
}
}
Custom Routes 2-5 ¶
These routes all involve the Work controller. So that is where we will add our event handlers.
class WorkController extends Controller
{
.. .
public function restEvents()
{
$this->onRest('req.get.special.render', function($param1) {
echo CJSON::encode(['param1'=>$param1]);
});
$this->onRest('req.put.special.render', function($data, $param1, $param2) {
//$data is the data sent in the PUT
echo CJSON::encode(['data'=>$data, $param1, $param2]);
});
$this->onRest('req.post.special.render', function($data, $param1) {
//$data is the data sent in the POST
echo CJSON::encode(['data'=>$data, 'param1'=>$param1, 'param2'=>$param2]);
});
$this->onRest('req.delete.special.render', function($param1, $param2) {
echo CJSON::encode(['param1'=>$param1, 'param2'=>$param2]);
});
}
Testing ¶
Running the project's automated tests.
Unit Tests ¶
- Make sure you that you have the correct database and database user in the test config (/WEB_ROOT/protected/extensions/starship/RestfullYii/tests/testConfig.php).
- % cd /WEB_ROOT/protected/extensions/starship/RestfullYii/tests
- % phpunit unit/
Contributors ¶
- evan108108
- goliatone
- ubin
- jlsalvador
- ericmcgill
- stianlik
- kachar
- drLev
- sheershoff
- Arne-S
- amesmoey
- eligundry
- rominawnc
Resources ¶
License ¶
Starship / RestfullYii is released under the WTFPL license - http://sam.zoy.org/wtfpl/. This means that you can literally do whatever you want with this extension.
Good extension
Will try to configure it and see how it works. Though had one question. In your example you said that Change your controller class so that it extends ERestController does it mean if I have PostController that extends from Controller should change to ERestController?.
Yes
That is right, so your PostController should look like this:
class PostController extends ERestController{..}
FYI: Class ERestController extends Controller
Also, don't forget to change the filters & accessRules in your PostController by prepending an "_".
`public function _filters()'
`public function _accessRules()'
Ok
I have configured the extension as you had described. But the models and my controllers are in a module and the urls keep on having issue.
http://localhost/cityhop.dev/api/classifieds/classifieds/
here is what I get.
{"success":false,"message":"You are not authorized to perform this action.","data":{"errorCode":401}}
Sample request.
You get this message when you have not set the username and password properly. It would be helpful if you could post a sample request here. Your request should contain the following:
"X_REST_USERNAME: admin@restuser"
"X_REST_PASSWORD: admin@Access"
Where:
X_[self::APPLICATION_ID]_USERNAME: [self::USERNAME]
X_[self::APPLICATION_ID]_PASSWORD: [self::PASWORD]
Module Compatibility
Hi bonnie,
I apologize, I misunderstood your question. The current version of RESTFullYii does not yet support use in Modules without modification. I will try and get module compatibility into the next version. In the mean time if you have come up with a solution please let me know so I can merge it in.
Thanks
-evan
Module Compatibility
Am trying out to get it will let you know but may try to change the section I want to use the extension to models and controllers in order to use it am really i need of it now.
Also I want to create a request can I use this extension Rest Client. I have found this in the forum maybe could be of a help to get you started Use http methods/verbs in your application rules
REST url rewrite rules
I think that something like this might give you idea:
'rules' => array( array('<module>/<controller>/read', 'pattern' => '<module:\w+>/<controller:\w+>/read/<id:\d+>', 'verb' => 'GET'), array('<module>/<controller>/list', 'pattern' => '<module:\w+>/<controller:\w+>/list/*', 'verb' => 'GET'), array('<module>/<controller>/update', 'pattern' => '<module:\w+>/<controller:\w+>/update/<id:\d+>', 'verb' => 'PUT'), array('<module>/<controller>/delete', 'pattern' => '<module:\w+>/<controller:\w+>/delete/<id:\d+>', 'verb' => 'DELETE'), array('<module>/<controller>/create', 'pattern' => '<module:\w+>/<controller:\w+>/create/', 'verb' => 'POST'), '<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>', )
You would need to have controller that would extend CContoller(you can create it in this extension), and I would somehow assign data from api request into model attributes in this base controller, and use existing model methods, like save, findAll and so on.
But the problem will be integration of this extension into existing code.
Amazing work! some hints?
I'd say it is an amazing extension.
I have 2 questions though:
1.My controller contains File Upload ... Can the API do this? Also, I do have the captcha in Create method....So what about the api...The user still need to put in the captcha, don't they?
2.This one is a little bit stupid but ....Would you please give some hints about how to do the auth? I want the user register for an API key and password. Should I just build it like a normal login system or I need to anything special?
Thanks so much for your amazing work!
wrong REST
actually you should not use like that
[POST] http://yoursite.com/api/post/search (returns posts matching post attributes)
it is wrong. Why ? because POST should change server state, but search does not do it. You should use obvious GET
Re: Wrong REST
That is true. Of course it breaks RESTFull conventions to use post for search. this is a method of convenience.
Filtercontext and userIdentity...
I have a filtercontext in my controller:
So each time, I want to create something, i need to have a url like this:
www.123.com/post/create?uid=4490 or www.123.com/post/create/uid/4490
What should I do with the api request?
Also,,,
Is there a way to combine the userIdentity...?
So that the user have to use his username/password to login without using a constant?
Best,
jiaming
$this->loadModel($id)
I see that this extension makes calls to a loadModel($id) method. Maybe I'm missing something, but I don't see this method anywhere in the ERestController class, or in the inherited CController class. Am I missing something?
In order to get this working, I've had to create a loadModel method which returns $this->getModel()->findByPk($id).
LoadModel
The
loadModel
method should be in your controller method. Remember that you should be extending your own controller with ERestController. If you have controller calledPostController
then you should do this:class PostController extends ERestController
Just make sure PostController has the loadModel method.
re: loadModel
evan108108 says:
> The loadModel method should be in your controller method.
The loadModel method isn't part of the base Controller or CController classes - why would this extension assume that I have added the loadModel method to my controller?
Bootstrap Extension Incompatibility
Hello, after install this extension and Bootstrap extension you will found that REST calls output is not fine because bootstrap preload their extansion and libraries. If you want to fix that, you have to set to false "coreCss", "yiiCss", "enableJS" and "responsiveCss" params in main.php and call to:
in your main layout to load just before rendering.
Fat model, Thin controller?
Is this respecting the MVC rule, Fat model thin controller, it seems not!! The code structure especialy for ERestController looks not ok for me.
Bootstrap incapability has been fixed.
Check out the latest version
The MVC Rule.
The MVC Rule: Skinny Controllers Fat Models. In general this is a great rule and I believe RestFullYii respects this rule. The reason for the MVC rule (amongst others) is that you typically do not instance controllers from outside of a their particular context, thus trapping methods that may be need externally in the controller class. RestFullYii is a base class for controllers you wish to add some RESTFUL behaviors to. RestFullYii's methods are there for available to any controllers that instance it. You get some additional "controller" functionality (functionality I believe belongs in the controller) while allowing you to keep your controllers Skinny Skinny!
If you don't agree with me and you think there is a better way to implement it great! Please go to GitHub (http://evan108108.github.com/RESTFullYii/), fork and submit a pull request. RestFullYii has had lots of contributors so far and we would love to have more.
CREATE example
public function actionCreateListCustom() {
$headers = array("Accept: application/json", "X-REST-USERNAME: admin@restuser", "X-REST-PASSWORD: admin@Access", "REQUEST_METHOD: POST"); $filename = Yii::app()->basePath . '/data/table03list.json'; $json_data = file_get_contents($filename); Yii::app()->CURL->options['setOptions'][CURLOPT_HTTPHEADER] = $headers; Yii::app()->CURL->options['setOptions'][CURLOPT_POSTFIELDS] = $json_data; $out = Yii::app()->CURL->run('http://localhost/owncurl01/app/index.php/api/table03/create'); echo $out; }
example Delete
public function actionDelete($id) {
$uri = 'http://localhost/xxx/app/index.php/api/table03/' . $id; $ch = curl_init($uri); curl_setopt_array($ch, array( CURLOPT_HEADER => FALSE, CURLOPT_HTTPHEADER => array('X-Rest-Username: admin@restuser', 'X-Rest-Password: admin@Access'), CURLOPT_RETURNTRANSFER => true, CURLOPT_VERBOSE => 1, CURLOPT_CUSTOMREQUEST => 'DELETE' )); $out = curl_exec($ch); curl_close($ch); echo $out; }
Please Clarify
Not sure what the previous two comments are referencing? This is not at all how you would use this extension. If you want to add a custom RESTFull route to your API its as simple as adding a method to your controller.
public function doCustomRestGetMyRoute($vars=array()) { //some logic $this->renderJson(array( 'success'=>true, 'message'=>'I called a custom route', 'data'=>$vars; )); }
You now have a route that looks like this:
~~~
http://yoursite.com/api/<controller>/myRoute/<optional_var1>/<optional_var2>
~~~
doCustomRestGet not doing it ...
hello Evan,
First off, I love this extension, used it many times. Keep up the good work.
So far, I was able to use your code as is, but now I need to create some custom GET requests, and I can't seem to figure out what I'm missing...
I tried to implement your example: doCustomRestGetOrder($var=null) in my controller, but I keep getting 404 errors:
{"success":false,"message":"The requested page does not exist.","data":{"errorCode":404}}
Do I need to set something specific in my routes/accessRules/filters?
I tried to troubleshoot it a little bit, noticed that you call triggerCustomRestGet() in actionRestView(), but the thing actually breaks before it gets to that function :/
thanks for your help,
--iM
Re: doCustomRestGet not doing it .
Hi imehesz,
I ran a test on my local and I can't replicate your problem, but I will go through the steps and you can confirm if this is what you are doing.
In your Controller (Lets say ItemController) add a method like this:
public function doCustomRestGetTesting($vars=null) { $this->renderJson(array('vars'=>$vars)); }
So your GET request will look like: http://mysite/api/item/testing/cool
This should return:
{
"vars": [
"cool", null
]
}
You can add a second param like: http://mysite/api/item/testing/cool/man
This should return:
{
"vars": [
"cool", "man"
]
}
I hope that helps. If its still not working I would be happy to take a look at your code and give you a hand...
doCustomRestGet working
Evan,
I had a custom beforeAction() in my controller, and that threw off the "REST" - (haha)
thanks for your help and for this extension!
--iM
Problem FIX-ex
If you have Model with Nested model, than in ERestController.php method loadOneModel should be:
protected function loadOneModel($id)
{
try { $a=$this->getModel()->with($this->nestedRelations)->findByPk($id); } catch(Exception $ex) { $a=null; } if ($a) { return $a; } else { throw new CHttpException(404, 'Record Not Found'); }
}
Otherwise PHP script return Error 500 Internal server error, for models who are not recorded in database :D
Enjoy, restfullyii -> fantastic extension
@stepanic
I don't think 'findByPk' with throw an exception when the PK is not found. I believe it just returns NULL...
Bug with update and nested relations
I found a bug when updating a record with nested relations. The requested relation id was over written by the original relation id.
This will fix that.
Changed in ERestController.php:
/** * Helper for loading a single model */ protected function loadOneModel($id, $nested=true) { if($nested) return $this->getModel()->with($this->nestedRelations)->findByPk($id); else return $this->getModel()->findByPk($id); }
And this in the same file:
/** * This is broken out as a sperate method from actionResUpdate * To allow for easy overriding in the controller * and to allow for easy unit testing */ public function doRestUpdate($id, $data) { $model = $this->saveModel($this->loadOneModel($id,false), $data); $this->outputHelper( 'Record Updated', $this->loadOneModel($id), 1 ); }
@Daantje
Can you give me a specific example? What exactly to you mean by "The requested relation id was over written by the original relation id"?
@evan108108
This is what I do: I post an update: {id:1,asset_id:3}
The original code gets the model with all the related/nested models.
So the model has now this: {id:1,asset_id:2,assets:{id:2,name:'john'}} (old record before save). Than there is a save, it saves the old asset_id, not the new one given in the $data param. But only when I have relations in the model.
So what rest api returns is this {id:1,asset_id:2,assets:{id:2,name:'john'}}
Other fields are saved correctly, only the indexed fields (in this case asset_id) are overwritten by the original value.
Git repo link is wrong
The link to your git repo is wrong. Might want to fix it.
@Tod C
The link to Git works for me. What happens when you click it?
ERestController.php -> public function triggerCustomRestPut
Should be:
public function triggerCustomRestPut($method, $vars=array())
{ $method = 'doCustomRestPut' . ucfirst($method); if(method_exists($this, $method)) { if(count($vars) > 0) $this->$method($this->data(), $vars); else $this->$method($this->data()); } else { throw new CHttpException(500, 'Method or Sub-Resource does not exist.'); }
}
Instead of:
public function triggerCustomRestPut($method, $vars=array())
{ $method = 'doCustomRestPut' . ucfirst($method); if(method_exists($this, $method)) { if(count($vars) > 0) $this->$method($this->data(), $vars); else $this->$method($this->data()); } throw new CHttpException(500, 'Method or Sub-Resource does not exist.');
}
If throw new CHttpException is out of ELSE statement, Custom PUT method will be never called.
@stepanic & @Daantje
I would like to thank the both of you for finding these bugs and submitting solutions! The new version 1.10 has your changes rolled in.
Data structure changed after upgrade from 1.9 to 1.10
I upgraded from 1.9 to 1.10 and it broke my application because the structure of the PUT response changed:
PUT {"name":"john"}
Version 1.9:
{ "success": true, "message": "Record Updated", "data": { "totalCount": 1, "person": { "id": "123", "name": "john", "update": { "id": "1", "version": "1.1.1" }, "group": [ { "id": "2", "name": "does" } ], "team": [ { "id": "3", "comment": "Yay" } ], } } }
Version 1.10:
{ "success": true, "message": "Record Updated", "data": { "totalCount": 1, "person": [ { "id": "123", "name": "john" } ] } }
Why is this happening?
Most notable is the change of the data type object becomes an array and I didn't even touch the database model etc. Also the many to many relations aren't visible anymore.
The structure of the GET output didn't change by the way.
I think I can handle the change but is this intentionally? I probably need to send another GET request because now I get less data than before.
using without AR
Hello, how can I use this extension with CModel? (I dont want to use ActiveRecord.)
Thank You!
@acy: Bug Fixes
Thanks you are correct. Version 1.11 should address these issues. As well as add a new feature which allows you to use Scenarios to control field output.
@heal
You can use it without active record. Just override the default actions in your controller IE doRestList, doRestView, etc...
data structure of PUT and GET still different
Thanks for the fix evan! The PUT request returns the missing relations now.
But one thing is still weird. The responses of a GET and a PUT have a different structure (see "person"):
GET /api/person/123/
{ "success": true, "message": "Record Retrieved Successfully", "data": { "totalCount": 1, "person": { "id": "123", "name": "john", "update": { "id": "1", "version": "1.1.1" }, "group": [ { "id": "2", "name": "does" } ], "team": [ { "id": "3", "comment": "Yay" } ], } } }
PUT /api/person/123/
{ "success": true, "message": "Record Updated", "data": { "totalCount": 1, "person": [ // <-- NEW { "id": "123", "name": "john", "update": { "id": "1", "version": "1.1.1" }, "group": [ { "id": "2", "name": "does" } ], "team": [ { "id": "3", "comment": "Yay" } ], } ] // <-- NEW } }
This happens only with 1.11 (not with 1.09)
Thanks a lot for your efforts!
@acy: Bug Fixes
Hi acy,
thanks again! version 1.12 should resolve the issue. Now Put and Get response data should be in insync.
advanced response filter
Hey there! Really great extension! Works well so far... I'm stuck now at some point during development. That's my (stripped) rest response:
GET /api/playlist/1
"playlist": { "id": "1", "name": "Playlist 1", "media": [ { "id": "2", "start_date": "2013-12-31 12:00:00", }, { "id": "3", "start_date": "2012-12-31 12:00:00", }, ], }
Now I want to modify/filter the response of the rest api. Using "filter" in the URI won't work as it is not listView.
In yii this scenario works well so far:
public function rules() { return array( array( 'start_date', 'compare', 'compareValue'=> date("Y-m-d H:i:s"), 'operator'=>'>=', 'on'=>'create' ), ); }
I need to show only data where 'start_date' >= today (in this case media #3 only) but I have no ideas how achieve this... Any suggestions?
Thanks in advance!
@acy: Re: advanced response filter
I have added a GET param simply called scenario. If you download the latest build of RESTFULLYii you should be able to do the following:
can you provide a demo ??
i have config the settings according to you guid , but it can not work correctly ,,thanks a lot , my email if changfuguo175464@163.com
include(Controller.php): failed to open stream: No such file or directory
hi ,
i make a file on the dir /protected/controller/MoiveController.php, the content are :
<?php
class MoiveController extends ERestController {
public function _filters(){
return array( );
}
public function _accessRules(){
return array( );
}
public function doRestList(){ echo "string"; }
}
,,my config setting are :
<?php
// This is the main Web application configuration. Any writable
// application properties can be configured here.
return array(
'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'name'=>'Yii Framework: EVA', // autoloading model and component classes 'import'=>array( 'application.models.*', 'application.components.*', 'application.lib.*', 'ext.restfullyii.components.*', ), // application components 'preload'=>array('log'), 'components'=>array( 'db'=>array( 'connectionString' => 'mysql:host=localhost;port=3306;dbname=0704', 'emulatePrepare' => true, 'username' => 'root', 'password' => '', 'charset' => 'utf8', ), 'urlManager'=>array( 'urlFormat'=>'path', 'rules'=>require(dirname(__FILE__).'/routes.php'), ), 'smarty' => array( 'class'=>'application.extensions.MySmarty', ), 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( /* array( 'class'=>'CFileLogRoute', 'levels'=>'error, warning', 'categories'=>'evaapk.*' ), */ array( 'class'=>'CWebLogRoute', 'levels'=>'fatal, warning', 'categories'=>'eva.*' ), ), ), ),
);
the routes config is under you guid setting , when my request is :http://localhost/yii/index.php/api/Moive , it should route the doRestList way defined on the movie controller , but the server throw an error like this :http://localhost/yii/index.php/api/Moive ,, how should i do ?
can you have some live demos , thank you very much ,,
Re: @fuguochang
Looks like the error your getting is that the Controller base class can not be found. Do you have a Controller.php base class in your components dir? If not you could change ERestController class to extend CController instead.
include(Controller.php): failed to open stream: No such file or directory
thanks for answer my question~
as you said , the url can route correct by add a function action 'show' on the sitecontroller , the content is as follow:
<?php
class SiteController extends BaseController
{
public function actionIndex() { } public function actionShow(){ //code here }
}
and the url is http://localhost/yii/index.php/site/show/tpl=index , the routes can work well .so the ccontroller should be no problem ,, i do not know whether my config is wrong ? i want to use you extension so so so , if you will, i can show you the detail by email, thanks a lot !!
Re: @fuguochang
I am not sure what your saying? The error you are getting is clear. The Controller class is not being found, correct?
Otherwise I think I might need to see your project to understand what is happening. If I can help I will.
hi
i mean that the website can route well when i wirte a function named show in the sitecontroller when visit , but can not work well when i write like this " class MoiveController extends ERestController " , the error message throw out ,if the ccontroller is not be included ,the show function can not work well , so i guess wheather is it other problem due to the error ?
can not find controller.php
thanks a lot ~ indeed , change ERestController class to extend CController instead. i am a beginer to use yii or php,so sorry to bother you ,and then ,is it necessary to set the RESTusername and RESTpassword??
Re: @fuguochang
Good I'm glad that helped.
You will need to either set the Rest user/password or if your using JS you may use the validateAJAXUser method.
You are a pro.
Wao maan... You saved my couple of days worth of work...
Keep up the good work..
Need a help for this
I have this function on my contoller:
public function actionListByCategory($id) { //$dataProvider=new CActiveDataProvider(ad::model()->with('subcategory')); //$this->render('index',array( // 'dataProvider'=>$dataProvider, //)); $dataProvider=new CActiveDataProvider('ad',array( 'criteria'=>array( 'order'=>'adid desc', 'condition'=>'subcatid = '.$id, ), 'pagination'=>array( 'pageSize'=>10, //'route'=>'user/login/login', ), )); $this->render('index',array( 'dataProvider'=>$dataProvider, )); }
But then how do execute this on the url on the browser with this /api/ - http://localhost:8888/dev-ph-com/webservice-dev-restful/index.php/ad/listbycategory/21
It doesnt' seem to work and we have this message: {"success":false,"message":"Resource Not Found","data":{"errorCode":404,"message":"Resource Not Found"}}
I believe this will be something in relation to routes maybe ? How do I get a support for this extension? I've tried posting at the forum and it seems no one really answering this question.
No relations data from json result
RestFullYii GET command (api/mywork) return complete json dataset with data relations model, how can i remove relationships data from results? in general how can i decide which data to display in json dataset?
Tnx.
@Domenico: Re No relations data from json result
This is easily accomplished. The Docs on GitHub will tell you all you need to know. That said here is an explanation:
In your controller add a method like so:
public function restEvents() { $this->onRest('model.with.relations', function($model) { $nestedRelations = []; return $nestedRelations; }); }
In this example you are hooking the event 'model.with.relations' and returning $nestedRelations as an empty array. This will remove all relations from the output. $nestedRelations can be a list of any relation you would like to include
HTTP/1.1 401 Unauthorized
Dear sir,
I have follow up your instruction and when I use GET action to get json data by curl in terminal, and I always get this error:
HTTP/1.1 401 Unauthorized
Please let me now where can I do?
HTTP/1.1 401 Unauthorized
Hi,
I got the same problem with curl authorization (windows 7, php 5.4 curl 7.33). I have created new Yii project and installed extensions with instructions but still have error: HTTP/1.1 401 Unauthorized
I have tried debugging app and one variable "$_SERVER['HTTP_X_'.$application_id.'_USERNAME']" seems to be unset/empty.
I tried te same app on debian with php 5.4 and te same problem, login by site works, by curl doesnt.
I lost idea what can be wrong.
Could you help me?
Syntax error in routes.php
The included routes.php (config folder), causes PHP syntax errors. This is because there are square brackets in the file and it does not return the routes as an array.
This happens in v1.22 and v1.23.
Anyone else experienced this also?
EDIT: In fact in many files the square brackets seem to cause syntax errors. I am wondering if something is wrong on my side...
Nevermind... Just figured out this is the shorthand array syntax. My system is not upgraded to PHP 5.4 yet...:(
Cheers! Sacha
@stelgenhof Re: Syntax error in routes.php
This syntax is perfectly valid as this extension requires php5.4+ . Seems like you are using an older version of PHP. I recommend that you upgrade to 5.4 or greater.
How to authenticate
I want to create an api for fetch user data and other information from the system. In request system will get authentication data (id,username and password) with i have in a table. I want to validate this data against db for each request.
Request format is as
{ "request": { "signin": { "id": "12345", "username": "test", "password": "xyz" }, "memberid": "03000000015", "remarks": { "remark": "check user request remark" } } }
How i can authenticate user with this extension ?
works only with "path" urlFormat ?
This is an excellent extension, and a must have (as backend) when using modern JS frameworks like AngularJS.
However, our team develops with urlFormat = get, and from the looks of it, this extension doesn't support both get and path urlFormat..
How would you go about implementing such a feature properly, so the extension would work with both settings?
@radoo: Get Routes
Sure its supported, simply modify the routes to your liking.
If you change the urlFormat to 'get' you can always do something like
myController/REST.GET
myController/REST.GET&id=1
myController/REST.GET&id=1¶m1=XXX¶m2=XXX
myController/REST.POST
myController/REST.PUT&id=1
myController/REST.DELETE&id=1
etc...
Controller Example
Can anyone who has implemented this please provide an example controller and how to call the actions? I need to call an API to validate a user and then that user be able to call the api and actions to get data from a database. I am not exactly sure how this is done.
can you provide us a whole controller?
i used your's old version , and it is can work well,so i want to use the latest version,but i do not know how to write a controller ,can you provide us a simple example ?
with the new version ,should not we write the action ??
the error , but we can not write a function named "REST.GET" ,??
The system is unable to find the requested action "REST.GET".
why i get Unauthorized result json ?
{
success: false,
message: "Unauthorized",
data: {
errorCode: 401,
message: "Unauthorized"
}
}
here is my controller ,i just copy form you guid , i do realy not know how to use the new version? please help me
<?php
class WorkController extends Controller{ public function filters() { return array( 'accessControl', // perform access control for CRUD operations array( 'ext.starship.RestfullYii.filters.ERestFilter + REST.GET, REST.PUT, REST.POST, REST.DELETE' ), ); } public function actions(){ return array( 'REST.'=>'ext.starship.RestfullYii.actions.ERestActionProvider' ); } public function accessRules(){ return array( array( 'allow','actions'=>array('REST.GET','REST.PUT','REST.POST','REST.DELETE'),'users'=>array('*'), ), array('deny','users'=>array('*')), ); } public function restEvents() { $this->onRest('req.get.special.render', function($param1) { echo CJSON::encode(['param1'=>$param1]); }); $this->onRest('req.put.special.render', function($data, $param1, $param2) { //$data is the data sent in the PUT echo CJSON::encode(['data'=>$data, $param1, $param2]); }); $this->onRest('req.post.special.render', function($data, $param1) { //$data is the data sent in the POST echo CJSON::encode(['data'=>$data, 'param1'=>$param1, 'param2'=>$param2]); }); $this->onRest('req.delete.special.render', function($param1, $param2) { echo CJSON::encode(['param1'=>$param1, 'param2'=>$param2]); }); }
public function actionrestGet(){ exec('source ~/.bash_profile; errHunter /home/qec/test' ,$result, $ret); echo json_encode($result); }
}
@fuguochang
Your clue is 'errorCode: 401, message: "Unauthorized"'.
RESTFullYii has several built in authentication mechanisms (See: req.auth.ajax.user, req.auth.ajax.user, req.auth.cors).
I am guessing you are trying to access this via your browser? If so you could either login and try again as req.auth.ajax.user will by default allow logged in users or hook the 'req.auth.ajax.user' auth event in your controller and simply return true to allow all users full access (be very careful!):
$this->onRest('req.auth.ajax.user', function() { return true; });
Other questions
i am so lucky that you can reply my questoins;and i reslove the problem by using you way; but i have other questions:
here i have a uri :xx.xx.com/api/job ,verb is get and the model is job ,the controller is
JonController
1、indeed,the uri above can give us the json data ,but here i want to add some business logic befor return the json data ,where can i add the code ? and which event can i use? because here does not use any resource , it is just a default action .
2、i think job are the resouces , because the default return data are linked to model 'Job';but you provide the event like req.get.resouce.render, it is seems for the config :'api/<controller:\w+>'=>['/REST.GET', 'verb'=>'GET'], we can define many resources,here for the 'Job' Model and event 'req.get.list.render' , is a kind resource for getting the list of Model Job , but for event 'req.get.list1.render' is another kind resource for Model Job , I don not know Whether my understanding is correct
3、how to distinguish the event req.get.resource .render and req.get.resources.render , how can i use the the events , and what request i send , it is can be processed by the above events?
4、i noticed that you can not proccess the 'like' operator ,so if i want to add some parameters by myself ,for the above uri:xx.xx.com/api/job ,if i want to add some parameters, i can get parameters by Yii::app()->request , I don not know Whether my understanding is correct ??
recently i wanto use this extension on my project ,bu i can not understand the usage fully,so if you have example code you can provide me, thanks a lot,my email is changfuguo@qq.com
Using your extension the right way
Hi Evan,
I find your extension very useful, but I am very concerned on the right way to use your extension. Basically, I am using your extension by extending it in my controller so my controller would look like this:
class MyController extends ERestController{ ... // Default rest post but adding more data public function doRestCreate() { //my code } // Default rest put but adding more data public function doRestUpdate($id, $data) { //my code } // My custom rest post action public function doCustomRestCreateAction(){ //my code } }
I was wondering if I am doing the right thing. Hoping to get some of your insights.
Nested relations not parsed in json result?
Hi,
I have something strange. When I have nested relations, it's only parsing the direct relation, not the nested ones?
This is what I have:
$this->onRest('model.find.all', function($model) { return $model::model()->mine()->with(array('video:mine','video.cuepoints','video.cuepoints.question'))->findAll(); });
But I only get video model, not video->cuepoints and video->cuepoints->question:
[javascript] { "success":true, "message":"Record(s) Found", "data":{ "totalCount":"4", "videoInvites":[ { "id":"3", "video_id":"7", "email":"", "user_id":"2", "video":{ "id":"7", "user_id":"1", "name":"How to make a paper plane" } } ] } }
When I var_dump the $model it has all relations in the dataset, so what am I missing? I'm using version 1.24.
@Daantje
Yes you are correct. Nested relationships are currently only one level deep. However you can simply catch the render event and do what you would like.
//Code not tested... $this->onRest('post.filter.model.with.relations', function($relations){ if($this->controller->action->id == 'REST.GET') { return ['video.mine','video.cuepoints','video.cuepoints.question']; } return $relations; }); $this->onRest('req.get.resource.render', function($data, $model_name, $relations, $count, $visibleProperties=[], $hiddenProperties=[]) { //RENDER CODE HERE! });
@evan108108
I've tried your solution, with a little correction, but it's throwing exceptions. It just doesn't want to go deeper into my relations... Maybe that's a good thing ;)
This is the error I'm getting:
~~~
[javascript]
{"success":false,"message":"Property \"VideoInvites.video.cuepoints\" is not defined.","data":{"errorCode":500,"message":"Property \"VideoInvites.video.cuepoints\" is not defined."}}
~~~
This is my restEvents method:
public function restEvents() { $this->onRest('config.dev.flag', function() { return true; }); $this->onRest('model.instance', function() { return new VideoInvites('mine'); }); $this->onRest('model.find.all', function($model) { $data = $model::model()->mine()->with(array('video:mine','video.cuepoints','video.cuepoints.question'))->findAll(); foreach($data as $k=>$m){ if($m->video == null){ unset($data[$k]); } } return $data; }); $this->onRest('post.filter.model.with.relations', function($relations){ if($this->getAction()->getId() == 'REST.GET') { return ['video','video.cuepoints','video.cuepoints.question']; } return $relations; }); $this->onRest('req.get.resources.render', function($data, $model_name, $relations, $count, $visibleProperties=[], $hiddenProperties=[]) { $this->setHttpStatus(((count($data) > 0)? 200: 204)); $this->renderJSON([ 'type' => 'rest', 'success' => (($count > 0)? true: false), 'message' => (($count > 0)? "Record(s) Found": "No Record(s) Found"), 'totalCount' => $count, 'modelName' => $model_name, 'relations' => $relations, 'visibleProperties' => $visibleProperties, 'hiddenProperties' => $hiddenProperties, 'data' => $data, ]); }); }
EDIT
This is working for me for now. I've used this solution. And changed my event methot to this:
public function restEvents() { $this->onRest('model.instance', function() { return new VideoInvites('mine'); }); $this->onRest('model.find.all', function($model) { $data = $model::model()->mine()->with(array('video:mine','video.cuepoints','video.cuepoints.question'))->findAll(); foreach($data as $k=>$m){ if($m->video == null){ unset($data[$k]); } } return $data; }); $this->onRest('req.get.resources.render', function($data, $model_name, $relations, $count, $visibleProperties=[], $hiddenProperties=[]) { $this->setHttpStatus(((count($data) > 0)? 200: 204)); header('Content-type: application/json'); echo CJSON::encode(['success'=>true, 'message'=>'Record(s) Found', 'data'=>[ 'totalCount'=>count($data), $model_name=>$data ]]); }); }
That is working for me. Maybe this is a solution for the extension to?
--Daantje
where is this "WorkController"?
It would be useful to see the WorkController... I am confused as how PUT,GET,DELETE works. Ie: how does this extension know which model to perform these actions on?
not saving data
Hi there, I have implemented RestfullYii, and got it working. However, when I do a post, it returns a success, even with a new ID, but when i look in the database, the record is not created.
What could be the problem?
Example
Hi guys,
I don't understand how to implement this. Could someone provide me an example?
Thanks,
ERestResourceHelper.php:148 $data is null when I PUT data
Hi there,
When I try to update, an error is being generated
it says that Invalid argument supplied for foreach() (starship/RestfullYii/components/ERestResourceHelper.php:148)
On closer inspections, it seams that $data is null in the setAttributes function.
Can you shed some light no why my put data is not being transfered into the $data variable?
The system is unable to find the requested action "rest.get".
hi
i get this error
The system is unable to find the requested action "rest.get".
i use php 5.5
yii 1.1.14
my urlManager was
'urlManager' => array(
'urlFormat' => 'path', 'showScriptName' => false,
'caseSensitive' => false,
'rules' => require( dirname(__FILE__).'/../extensions/starship/RestfullYii/config/routes.php' ), ),
i change it to
'urlManager' => array(
'urlFormat' => 'path', 'showScriptName' => false, 'rules' => require( dirname(__FILE__).'/../extensions/starship/RestfullYii/config/routes.php' ), ),
and its work
maybe it help you
Layout/main.php is also rendered
The Extension is working like a charm. But the response cotains "layout/main.php"(with menus and stuffs) in which the content part is the json array of results. How can i avoid rendering "layout/main.php".
Thanks in advance,
Nivas
Don't want relation in get Request
Can any one please tell me how can I restrict data of relation in get request
re @Urvish
Its very easy.
If you don't want the entire relation you can simply use the 'post.filter.model.with.relations' event:
$this->onRest('post.filter.model.with.relations', function($result)) { return array_filter($result, function($val) { return ($val != 'NAME_OF_REALTION_I_WANT_TO_REMOVE'); }); });
If you just want to hide some of the fields in your relation you can use
$this->onRest('model.hidden.properties', function() { return ['MY_RELATION.FIELD_1_TO_HIDE', 'MY_RELATION.FIELD_2_TO_HIDE']; });
Just check the documentation for instructions on using events. Hope that helps...
How (if it) does this integrate with the Rights module?
I was wondering if anyone has gotten into combining this with the Rights module enabled or not. As the rights module sort of takes over the filters() themselves and handles the availability or just assignments of access roles (guest, admin, authenticated, etc) which will have to be included from restfulyii as well, I'm not quite sure how to go about it and I haven't found a resource online yet.
Please give a detailed answer if you do know how. I am still a novice but trying hard to understand the overall workings and features of Yii and the plugins. Being a big site I'm building I decided to go with Rights other than having to specify action rights in each controller.
Thank you in advance for your help.
accesControl on restEvents
He there,
I've been using restfullyii for a while now, and everything is working great.
I was just wondering if the following is possible:
accessRules is looking like this:
public function accessRules() { return array( array('allow', 'actions'=>array('REST.GET.SPECIAL'), 'users'=>array('@'), ), array('allow', // allow admin user to perform 'admin' actions 'actions'=>array('REST.GET', 'REST.PUT', 'REST.POST', 'REST.DELETE'), 'users'=>Yii::app()->getModule('user')->getAdmins(), ), array('deny', // deny all users 'users'=>array('*'), ), ); }
restEvents is looking like this:
public function restEvents() { $this->onRest('req.get.special.render', function() { /// blabla code }); }
I can't seem to find it in the documentation.
Thanks a lot!
Does not work with STAT relationship
Using the blog example, when I tried to include a STAT relation on postCount, it gives me "Trying to get property of non-object" error. Anyone encountered similar problem?
class User extends CActiveRecord { ..... /** * @return array relational rules. */ public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'posts' => array(self::HAS_MANY, 'Post', 'author_id'), // 'postCount' => array(self::STAT, 'Post', 'author_id'), ); } .... }
Authentication for custom routes using both action and route name
From docs, "post.filter.req.auth.ajax.user" or "post.filter.req.auth.user" can be used to authenticate user based on actionId, i.e. REST.GET, REST.POST, REST.PUT, REST.DELETE. Can we filter by the route name too?
For example, I have a custom route "req.get.public.render" which any user can access, but another custom route "req.get.secret.render" which requires authentication.
$this->getAction()->getId() returns REST.GET, REST.POST, REST.PUT, REST.DELETE. How to get the custom route name? i.e. "public" or "secret"?
Get contents of a post call
Hello,
I am making a post call like this ..
curl -i -H "Accept: application/json" -H "HTTP_X_REST_USERNAME: hello" -H "HTTP_X_REST_PASSWORD: world" -X POST -d '{"time":"2014-09-08 11:23:00","task":"run","notes":"HELP Needed"}' http://localhost:8888/workspace/pharma/index.php/api/apiTest
I am not able to read the contents of POST data
$this->onRest('req.data.read', function($stream='php://input') { echo "$stream"; $reader = new ERestRequestReader($stream); print_r(CJSON::decode($reader->getContents())); return CJSON::decode($reader->getContents()); });
What is the right way to get the post data?
Thanks,
Shekar
Adding to previous comment
print_r($_POST) yields an empty array.
file upload
it's nice to add some ability for file uploading
API Test using curl on command prompt
[shell]
curl -i -H "Accept: application/json" -H "X_REST_USERNAME: admin@restuser" -H "X_REST_PASSWORD: admin@Access"\
http://my-site.com/api/work
I had wasted my time a lot while testing this application on command prompt.
I have Macbook pro system.
But finally i got the application running using curl on command prompt
the above curl syntax have a typo error, please use the below code:-
[shell]
curl -i -H "Accept: application/json" -H "X-REST-USERNAME:admin@restuser" -H "X-REST-PASSWORD:admin@Access" http://my-site.com/api/work
Thanks
Ranjita
please help!!!!! the below code is not working
$config = array(
.., 'params'=>[ 'RestfullYii' => [ 'req.auth.user'=>function($application_id, $username, $password) { return false; }, ] ]
);
The above code is giving error, when i am adding to congif/main.php (yii 1.16)
Error:-
CException
Property "CWebApplication.0" is not defined.
/Applications/XAMPP/xamppfiles/htdocs/yiirestapi/framework/base/CModule.php(520)
508 $this->setComponent($id,$component,$merge);
509 }
510
511 /*
512 Configures the module with the specified configuration.
513 @param array $config the configuration array
514 /
515 public function configure($config)
516 {
517 if(is_array($config))
518 {
519 foreach($config as $key=>$value)
520 $this->$key=$value;
521 }
522 }
523
524 /*
525 Loads static application components.
526 */
527 protected function preloadComponents()
528 {
529 foreach($this->preload as $id)
530 $this->getComponent($id);
531 }
532
=============== line no 520 has error ===============
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.