Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...


We define some of convention way to mapping from a data source to resource's properties

Map resource's properties by naming convention:
If the data source has key same with the property name. It's auto mapped

Map with User Resource
If the data source has a set of "user_" prefix key and `user` property, It auto combine field to UserResource and Map to user property of the current resource

Manual mapping
Use We can override or manual mapping fields use Setter and Getter methods. Setter method uses to control input data, Getter control the output. The naming convention of setter & getter methods follow examples bellowAll field

Resource's property nameSetter methodGetter method
namesetName()getName()
short_descriptionsetShortDescription()getShortDescription()
is_approvedsetIsApproved()getIsApproved()

 

All fields query from the database table has a String data type. But Native App requires to return exactly data type in JSON.
Override "loadMetadataSchema" method of resource to control the response data type.


Following is an example:

Code Block
<?php

/* A lot of code above */
class PostResource extends ResourceBase
{
/* ... */
	protected function loadMetadataSchema(ResourceMetadata $metadata = null)
	{
		parent::loadMetadataSchema($metadata);
		$this->metadata
		    ->mapField('title', ['type' => ResourceMetadata::STRING])
		    ->mapField('description', ['type' => ResourceMetadata::STRING])
		    ->mapField('item_id', ['type' => ResourceMetadata::INTEGER])
		    ->mapField('module_id', ['type' => ResourceMetadata::STRING])
		    ->mapField('is_approved', ['type' => ResourceMetadata::BOOL])
		    ->mapField('is_sponsor', ['type' => ResourceMetadata::BOOL])
		    ->mapField('is_featured', ['type' => ResourceMetadata::BOOL])
		    ->mapField('is_liked', ['type' => ResourceMetadata::BOOL])
		    ->mapField('is_friend', ['type' => ResourceMetadata::BOOL])
		    ->mapField('post_status', ['type' => ResourceMetadata::INTEGER]);
	}
}

...

Code Block
<?php 

/* A lot of code above */
class PostResource extends ResourceBase
{
/* ... */
/**
Get detail url
@return string
*/
	public function getLink()
	{
		return \Phpfox::permalink('post', $this->id, $this->title);
	} 
	public function getImage()
	{
		return Image::createFrom([
			'file' => $this->rawData['image_path'],
			'server_id' => $this->rawData['server_id'],
			'path' => 'post.url_photo',
			'suffix' => '_1024'
		]);
	} 
/**
@return array
@throws \Exception
*/
	public function getCategories()
	{
		return $this->categories;
	} 
	public function getTags()
	{
		return $this->tags;
	} 
	public function getText()
	{
		if (empty($this->text) && !empty($this->rawData['text'])) {
			$this->text = $this->rawData['text'];
		}
		TextFilter::pureHtml($this->text, true);
		return $this->text;
	}
    /* ... */
} 

Define API service to handle API requests

...

Code Block
<?php
namespace Apps\Posts\Service; 
/* ... */ 
class PostApi extends AbstractResourceApi implements ActivityFeedInterface, MobileAppSettingInterface
{
/**
@var Post
*/
	private $postService; 
	/**
	@var Process
	*/
	private $processService; 
	/**
	@var Category
	*/
	private $categoryService; 
	/**
	@var \User_Service_User
	*/
	private $userService; 

	public function __construct()
	{
		parent::__construct();
		$this->postService = Phpfox::getService("post");
		$this->categoryService = Phpfox::getService('post.category');
		$this->processService = Phpfox::getService('post.process');
		$this->userService = Phpfox::getService('user');
	}
    /* .. */
}

In  

Describe the example above:

  • The PostApi class extent AbstractResourceApi to reuse resource base featureall features
  • Implement ActivityFeedInterface to able display the resource to Activity Feed page
  • Implement MobileAppSettingInterface to register more screens and actions to Mobile App without change code

Now you need to implement all abstract method from parent class and interfaces.
The initial code of your service would look like bellowlike below:


PostApi.php  

Code Block
<?php 

namespace Apps\Posts\Service;
/* ... */ 
class PostApi extends AbstractResourceApi implements ActivityFeedInterface, MobileAppSettingInterface
{
	/**
	 * Get list post
	 *
	 * @param array $params
	 * @return array|mixed
	 * @throws ValidationErrorException
	 */
	function findAll($params = [])
	{
	    /* ... */
		$aItems = $this->browse()->getRows();
		if ($aItems) {
			$this->processRows($aItems);
		} 
		return $this->success($aItems);
	} 
	
    /**
	 * @param $params
	 * @return array|bool
	 */
	function findOne($params)
	{
		$id = $this->resolver->resolveId($params);
		/* ... */
		return $this->success($resource->loadFeedParam()->toArray());
	} 

	public function delete($params)
	{
		$id = $this->resolver->resolveId($params); 
		$result = Phpfox::getService('post.process')->delete($id);
		if ($result !== false) {
			return $this->success([
				'id' => $id
			]);
		}
		return $this->error('Cannot delete post'); 
	} 
	
    /**
	 * Get Create/Update document form
	 * @param array $params
	 * @return mixed
	 * @throws \Exception
	 */
	public function form($params = [])
	{
		/** @var PostForm $form */
		$form = $this->createForm(PostForm::class, [
			'title' => 'adding_a_new_post',
			'method' => 'post',
			'action' => UrlUtility::makeApiUrl('post')
		]);
		/* ... */
		return $this->success($form->getFormStructure());
	} 
	/**
	 * Create a new Post API
	 * @param array $params
	 * @return array|bool|mixed
	 */
	public function create($params = [])
	{
	  /* ... */
	} 
	
    /**
	 * Update a post
	 *
	 * @param $params
	 * @return mixed
	 */
	public function update($params)
	{
	 /* ... */
	} 

	/**
	 * @param $id
	 * @param bool $returnResource
	 * @return array|PostResource
	 */
	function loadResourceById($id, $returnResource = false)
	{
		$item = Phpfox::getService("post")->getPost($id); 
		if (empty($item['post_id'])) {
			return null;
		}
		if ($returnResource) {
			return $this->processOne($item);
		}
		return $item;
	} 
	/**
	 * Update multiple document base on document query
	 *
	 * @param $params
	 * @return mixed
	 * @throws \Exception
	 */
	public function patchUpdate($params)
	{
		/* ... */
	} 


	/**
	 * Get for display on activity feed
	 * @param array $feed
	 * @param array $item detail data from database
	 * @return array
	 */
	public function getFeedDisplay($feed, $item)
	{
		/* ... */
	} 

	/**
		 * Create custom access control layer
	 */
	public function createAccessControl()
	{
		$this->accessControl = new PostAccessControl($this->getSetting(), $this->getUser()); 
		/* ... */
	} 
	
    /**
	 * @param array $params
	 * @return mixed
	 */
	function searchForm($params = [])
	{
		$this->denyAccessUnlessGranted(PostAccessControl::VIEW);
		/** @var PostSearchForm $form */
		$form = $this->createForm(PostSearchForm::class, [
			'title' =>'search',
			'method' => 'GET',
			'action' => UrlUtility::makeApiUrl('post')
		]); 
		return $this->success($form->getFormStructure());
	} 

	public function getRouteMap()
	{
		/* ... */
	} 
	
    /**
	 * @param $param
	 * @return MobileApp
	 */
	public function getAppSetting($param)
	{
		/* ... */
	}
}

...

Code Block
<?php

/* ... */
Phpfox::getLib('module')
	->addAliasNames('post', 'Posts')
	->addServiceNames([
		    
    // New API service register here
	'mobile.post_api' => Service\PostApi::class,
	'mobile.post_category_api' => Service\PostCategoryApi::class, 
		
    // Other Services of the app
	'post.category' => Service\Category\Category::class,
	'post.category.process' => Service\Category\Process::class,
	'post.api' => Service\Api::class,
	'post' => Service\Posts::class,
	'post.browse' => Service\Browse::class,
	'post.cache.remove' => Service\Cache\Remove::class,
	'post.callback' => Service\Callback::class,
	'post.process' => Service\Process::class,
	'post.permission' => Service\Permission::class,
	]);
/* ... */

...

Code Block
<?php

class PostApi extends AbstractResourceApi implements ActivityFeedInterface, MobileAppSettingInterface
{
    /**
    This method allow you add custom route for APIs
    Return an array with key is routing rule and mapping condition
    In this case, 'mobile/post/search-form' map to `searchForm` method
    */
    public function __naming()
    {
	    return [
		    'post/search-form'=> [
		    'get'=>'searchForm'
		    ]
	    ];
    }

    /**
    @param array $params
    @return mixed
    */
    function searchForm($params = [])
    {
	    $this->denyAccessUnlessGranted(PostAccessControl::VIEW);
	    /** @var PostSearchForm $form */
	    $form = $this->createForm(PostSearchForm::class, [
		    'title' =>'search',
		    'method' => 'GET',
		    'action' => UrlUtility::makeApiUrl('post')
		    ]); 
	    return $this->success($form->getFormStructure());
    }
}

You can download the full sample code of Post API service.

...

Postman is one of a good application for testing APIs. 
Here are the API patterns to access Posts app

GET url

 

Or get Edit form response 
GET url
 

 
 
 
 


 


 
 
true, 
 
 
 
 
}, 

 
 
true, 
 
 
 
 
}, 

 
 
 
 
null, 
null, 
 


}, 

 


 
 
true, 
 
 
 


1, 
 
}, 

2, 
 
}, 

3, 
 

], 
 
}, 

 
 
 
 
 
 
}, 

 
 
 
 
 
null, 
 


}, 

 


 
 
0, 
 


 
"value": 0 
}, 

"label": "Friends", 
"value": 1 
}, 

"label": "Friends of Friends", 
"value": 2 
}, 

"label": "Only Me", 
"value": 3 
}, 

"label": "Custom", 
"value": 4 

], 
"label": "Privacy", 
"multiple": false, 
"description": "Control who can see this post" 



}, 
"fields": { 
"module_id": { 
"name": "module_id", 
"component_name": "Hidden", 
"value": "post" 
}, 
"item_id": { 
"name": "item_id", 
"component_name": "Hidden" 
}, 
"draft": { 
"name": "draft", 
"component_name": "Checkbox", 
"value": 0, 
"label": "Save as Draft" 
}, 
"submit": { 
"name": "submit", 
"component_name": "Submit", 
"label": "Publish", 
"value": 1 



}DELETE url
Patterndescriptionex. response
Code Block
languagebash
GET {{url}}/restful_api/mobile/post?access_token={{token}}
Get List of Posts{
Code Block
languagejs
collapsetrue
{
    "status": "success",


    "data":
[
{
 [
        {
            "resource_name": "post",


            "title": "Title of the post",


            "description": "Description of the post",


            "module_id": "post",


            "item_id": 0,


            "is_approved": true,


            "is_sponsor": false,


            "is_featured": false,


            "is_liked": null,


            "is_friend": null,


            "post_status": 1,


            "image": null,


            "statistic":
{
 {
                "total_like": 0,


                "total_comment": 0,


                "total_view": 1,


                "total_attachment":
0
},
 0
            },
            "privacy": 0,


            "user":
{
 {
                "full_name": "Cute User",


                "avatar": null,


                "id":
1
},
 1
            },
            "categories":
[
{
 [
                {
                    "id": 6,


                    "name": "Recreation"

}
],

                }
            ],
            "tags": [],


            "attachments": [],


            "id": 1,


            "creation_date": "2018-11-30T04:44:39+00:00",


            "modification_date": null,


            "link": "http://localhost:7788/post/1/love/",


            "extra":
{
 {
                "can_view": true,


                "can_like": true,


                "can_share": true,


                "can_delete": true,


                "can_report": false,


                "can_add": true,


                "can_edit": true,


                "can_comment": true,


                "can_publish": false,


                "can_feature": true,


                "can_approve": true,


                "can_sponsor": true,


                "can_sponsor_in_feed": false,


                "can_purchase_sponsor":
true
},
 true
            },
            "self": null,


            "links":
null
}
]
}GET url
 null
        }
    ]
}
Code Block
languagebash
GET {{url}}/restful_api/mobile/post/1?access_token={{token}}
Get Post Detail Expand source
{
Code Block
languagejs
collapsetrue
{
    "status": "success",


    "data":
{
 {
        "resource_name": "post",


        "title": "Title of the post",


        "description": "Description of the post",


        "module_id": "post",


        "item_id": 0,


        "is_approved": true,


        "is_sponsor": false,


        "is_featured": false,


        "is_liked": false,


        "is_friend": false,


        "post_status": 1,


        "text": "xxxx",


        "image": null,


        "statistic":
{
 {
            "total_like": 0,


            "total_comment": 0,


            "total_view": 1,


            "total_attachment":
0
},
 0
        },
        "privacy": 0,


        "user":
{
 {
            "full_name": "Cute User",


            "avatar": null,


            "id":
1
},
 1
        },
        "categories":
[
{
 [
            {
                "id": 6,


                "name": "Recreation"

}
],

            }
        ],
        "tags": [],


        "attachments": [],


        "id": 1,


        "creation_date": "2018-11-30T04:44:39+00:00",


        "modification_date": null,


        "link": "http://localhost:7788/post/1/love/",


        "extra":
{
 {
            "can_view": true,


            "can_like": true,


            "can_share": true,


            "can_delete": true,


            "can_report": false,


            "can_add": true,


            "can_edit": true,


            "can_comment": true,


            "can_publish": false,


            "can_feature": true,


            "can_approve": true,


            "can_sponsor": true,


            "can_sponsor_in_feed": false,


            "can_purchase_sponsor":
true
},
 true
        },
        "self": null,


        "links":
{
 {
            "likes":
{
 {
                "ref": "mobile/like?item_type=post&item_id=1"

},

            },
            "comments":
{
 {
                "ref": "mobile/comment?item_type=post&item_id=1"

}
},

            }
        },
        "feed_param":
{
 {
            "item_id": 1,


            "comment_type_id": "post",


            "total_comment": 0,


            "like_type_id": "post",


            "total_like": 0,


            "feed_title": "Love",


            "feed_link": "http://localhost:7788/post/1/love/",


            "feed_is_liked": false,


            "feed_is_friend": false,


            "report_module": "post"

}

}
}


        }
    }
}
Code Block
languagebash
GET {{url}}/restful_api/mobile/post/form/?access_token={{token
}}
 
# Or get Edit form response
GET {{url}}/restful_api/mobile/post/form/1?access_token={{token}}
Get create post form
Code Block
languagejs
collapsetrue
{
    "status": "success",

    "data":
 {
        "title": "Add a new post",

        "description": "",

        "action": "mobile/post",

        "method": "post",

        "sections":
 {
            "basic":
 {
                "label": "Basic Info",

                "fields":
 {
                    "title":
 {
                        "name": "title",

                        "component_name": "Text",

                        "required":
 true,
                        "value": "",

                        "returnKeyType": "next",

                        "label": "Title",

                        "placeholder": "Fill title for post"

                    },
                    "text":
 {
                        "name": "text",

                        "component_name": "TextArea",

                        "required":
 true,
                        "value": "",

                        "returnKeyType": "default",

                        "label": "Post",

                        "placeholder": "Add content to post"

                    },
                    "attachment":
 {
                        "name": "attachment",

                        "component_name": "Attachment",

                        "label": "attachment",

                        "item_type": "post",

                        "item_id":
 null,
                        "current_attachments":
 null,
                        "upload_endpoint": "mobile/attachment"

                    }
                }
            },
            "additional_info":
 {
                "label": "Additional Info",

                "fields":
 {
                    "categories":
 {
                        "name": "categories",

                        "component_name": "Choice",

                        "multiple":
 true,
                        "value": [],

                        "label": "Categories",

                        "value_type": "array",

                        "options":
 [
                            {
                                "value":
 1,
                                "label": "Business"

                            },
                            {
                                "value":
 2,
                                "label": "Education"

                            },
                            {
                                "value":
 3,
                                "label": "Entertainment"

                            }
                        ],
                        "suboptions": []

                    },
                    "tags":
 {
                        "name": "tags",

                        "component_name": "Tags",

                        "value": "",

                        "returnKeyType": "next",

                        "label": "Topics",

                        "description": "Separate multiple topics with commas."

                    },
                    "file":
 {
                        "name": "file",

                        "component_name": "File",

                        "label": "Photo",

                        "file_type": "photo",

                        "item_type": "post",

                        "preview_url":
 null,
                        "upload_endpoint": "mobile/file"

                    }
                }
            },
            "settings":
 {
                "label": "Settings",

                "fields":
 {
                    "privacy":
 {
                        "name": "privacy",

                        "component_name": "Privacy",

                        "value":
 0,
                        "returnKeyType": "next",

                        "options":
 [
                            {
                                "label": "Everyone",

                                "value": 0
                            },
                            {
                                "label": "Friends",
                                "value": 1
                            },
                            {
                                "label": "Friends of Friends",
                                "value": 2
                            },
                            {
                                "label": "Only Me",
                                "value": 3
                            },
                            {
                                "label": "Custom",
                                "value": 4
                            }
                        ],
                        "label": "Privacy",
                        "multiple": false,
                        "description": "Control who can see this post"
                    }
                }
            }
        },
        "fields": {
            "module_id": {
                "name": "module_id",
                "component_name": "Hidden",
                "value": "post"
            },
            "item_id": {
                "name": "item_id",
                "component_name": "Hidden"
            },
            "draft": {
                "name": "draft",
                "component_name": "Checkbox",
                "value": 0,
                "label": "Save as Draft"
            },
            "submit": {
                "name": "submit",
                "component_name": "Submit",
                "label": "Publish",
                "value": 1
            }
        }
    }
}
Code Block
languagebash
DELETE {{url}}/restful_api/mobile/post/{{post_id}}?access_token={{token}}
Delete a Post