I am in a problem of modifying the cake association hierarchy,I am new to cakephp , below is the snippet of code.
Current Code
$this->Cateogory->find('all');
The output of the above expression is of the below form :
[
{
"Category": {
"id": "37",
"title": "Inner Title",
"color": "#ffffff",
"phone-number": ""
},
"CategoryHas": [],
"PhoneNumberhas": []
}
]
Requirement :-
I am creating API for third party consumption , now the third party has given the output format as below. I want the the output to be of below form.
[
{
"Category": {
"id": "37",
"title": "Inner Title",
"color": "#ffffff",
"phone-number": "",
"CategoryHas": [],
"PhoneNumberhas": []
}
}
]
I am searching on this problem for past 1 day and I consider that I need to make a custom PHP function to solve the problem.
Thanks in advance :
Just use a loop
To convert the array of the first format to an array of the second format - just use a loop e.g.:
foreach ($stuff as &$row) {
$row['Category']['CategoryHas'] = $row['CategoryHas'];
$row['Category']['PhoneNumberhas'] = $row['PhoneNumberhas'];
unset($row['CategoryHas'], $row['PhoneNumberhas']);
}
This would be appropriate to put in your controller if it's only needed in one place.
If you always, always want your results in that format, use an afterFind method (or a behavior) to implement the same logic:
// In the category model
public function afterFind($results, $primary = false) {
foreach ($results as &$row) {
if (isset($row['CategoryHas'])) {
$row['Category']['CategoryHas'] = $row['CategoryHas'];
}
if (isset($row['PhoneNumberhas'])) {
$row['Category']['PhoneNumberhas'] = $row['PhoneNumberhas'];
}
unset($row['CategoryHas'], $row['PhoneNumberhas']);
}
return $results;
}
Be wary of choosing this option - it means that your models act unconventionally, potentially meaning plugins don't work and other developers (or you, tomorrow) are confused by the way the models act.
Your association names are pretty strange. Why are they not better named? You can use proper names internally and reformat it when sending it.
If this is just about getting the API response into a silly format that doesn't provide additional benefit use JSON views and change the structure of the array in the view as needed. This section of the book explains it in detail, read it. Example taken from the book:
// Controller code
class CategoriesController extends AppController {
public function index() {
// ... get the categories
$this->set(compact('categories'));
}
}
// View code - app/View/Posts/json/index.ctp
foreach ($categories &$row) {
if (isset($row['CategoryHas'])) {
$row['Category']['CategoryHas'] = $row['CategoryHas'];
}
if (isset($row['PhoneNumberhas'])) {
$row['Category']['PhoneNumberhas'] = $row['PhoneNumberhas'];
}
unset($row['CategoryHas'], $row['PhoneNumberhas']);
}
echo json_encode(compact('categories'));
I would convince the client and bring some arguments for a proper response format and structure. Most clients usually don't know what they want. Sometimes you have smart asses who think they need something specific but can explain why. Check JSend or http://jsonapi.org/ on how to implement a proper API response format. Understand the reasons for these formats and bring these arguments to the client.
Related
I am using Laravel (PHP) and MySQL for my backend. I am creating methods for setting and getting information from the database. Those information are being send as a json to the frontend.
I can send table information like:
[
{
"id": 4,
"name": "Max"
},
{
"id": 5,
"name": "Eric"
}
]
For this I am using laravel methods like: DB::table('user')->select('user.id', 'user.name')->get();
However my friend who is doing the frontend want the following json code:
[
{
"id": 4,
"name": "Max"
"specific_user_price":{
"S":5.00,
"M":6.00,
"XL":8.00
}
},
{
"id": 5,
"name": "Eric"
"specific_user_price":{
"S":5.50,
"M":10.00,
"XL":15.00
}
}
]
"specific_user_price is a table and "S", "M", "XL" are columns which have different values depending on the user. I do not know how I can create specific_user_price as an array in a query. I can use group_concat but he needs the json like displayed above.
My idea was to create additional columns in user "size S price", "size M price" and "size XL price". However my friend want those values as an own array group, because some users only habe access to one size, so he would get null values.
Any ideas which method in PHP or Laravel I can use for that? Or is there a MySQL method for creating such thing in a query?
Firstly use Models, way easier to work with out of the box. Define your User model like this, with a relationship for the price.
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function specificUserPrice() {
return $this->hasOne(UserPrice::class);
}
}
You also need to have the UserPrice model defined.
use Illuminate\Database\Eloquent\Model;
class SpecificUserPrice extends Model
{
}
Laravel automatically transforms models, you can get away with the following code in the controller.
public function index()
{
return User::with('specificUserPrice')->get();
}
DB::table('user')->select(['id', 4],['name','max'])->get();
DB::table('user')[0]->get()
This gets you the first element in the array
I have a web application where the user shall be able to change MariaDB records via a GUI inside the browser.
In this GUI, the user sees a list of the records visible for him. Whatever he can see, he has permission to delete or change as well.
To change a DB record, he simply has to click the fields he wants to change inside the list and then press the "send" button.
In his HTTP request, the id associated with the record will be transmitted so the backend can identify the respective record inside the DB and apply the changes.
Now, Im rather new to Laravel/Lumen. To fetch the list the user can apply changes to in the first place, I have the following code:
$join = coretable::with($permittedTables)->get();
The $permittedTables is an array of tablenames, so any number or combination of tables might be joined to coretable.
For example, a fetch can look like this:
[{
"Internal_key": "TESTKEY_1",
"extensiontable_itc": {
"description": "EXTENSION_iTC_1"
},
"extensiontable_sysops": {
"description": "EXTENSION_SYSOPS_1"
}
}, {
"Internal_key": "TESTKEY_2",
"extensiontable_itc": {
"description": "EXTENSION_ITC_2"
},
"extensiontable_sysops": {
"description": "EXTENSION_SYSOPS_2"
}
}, {
"Internal_key": "TESTKEY_3",
"extensiontable_itc": {
"description": "EXTENSION_ITC_3"
},
"extensiontable_sysops": {
"description": "EXTENSION_SYSOPS_3"
}
}, {
"Internal_key": "TESTKEY_4",
"extensiontable_itc": {
"description": "EXTENSION_ITC_4"
},
"extensiontable_sysops": {
"description": "EXTENSION_SYSOPS_4"
}
}, {
"Internal_key": "TESTKEY_5",
"extensiontable_itc": {
"description": "EXTENSION_ITC_5"
},
"extensiontable_sysops": {
"description": "EXTENSION_SYSOPS_5"
}
}]
Now, I wondered if I could just reuse the code I've created to fetch the data for setting the data.
So I used the above shown $join and tried to determine the datarecords I want to change.
The attempt looks like this:
$join = $join->find("TESTKEY_1");
The find however doesnt return anything.
Considering the structure of the results I'm querying here, is this approach even feasible? Or should I build some new code, fetching the results in a different structure, better suited to have changes applied to it?
Still, is there a way to search these fetchresults for a subset of data and then apply changes to this subset (and persist those changes to the DB, of course)?
$join = $join->find("TESTKEY_1");
find method works only for ids.
e.g. $join = $join->find(1); //1 is id (primary_key) in here.
if you want to find the record by TESTKEY_1
$join = $join->where("Internal_key","TESTKEY_1")->get();
// will return array of all records having Internal_key TESTKEY_1
if you want only the first record to be fetched
$join = $join->where("Internal_key","TESTKEY_1")->first();
// will return only first having Internal_key TESTKEY_1 as an array.
I am building API. I ran into issue when building responses such as this one:
{
"id": 1,
"name": "Some name",
"my_joined_table": {
"joined_table_id": "10",
"some_joined_table_field": "some value"
}
},
Joining tables as described in https://laravel.com/docs/5.2/queries would yield result such as:
{
"id": 1,
"name": "Some name",
"joined_table_id": "10",
"some_joined_table_field": "some value"
},
Instead of using join I could just run two queries, one for main table, second one for secondary and then just append second array to first one and spit JSON response, but it's a lot of queries and appending if list is big!
Example code which yields second result in pseudo-code:
$data = Model::select('id', 'name', 'my_joined_table.id as joined_table_id', 'my_joined_table.some_value some_value')
->leftJoin('my_joined_table', function($join) { //conditions_callback
})->get();
return response()->json($data);
Please advice.
EDIT2:
It seems that I can use with as follows:
$data = Model::with('my_second_table')->first();
return response()->json($data);
It does what I want, only the problem, that I cannot specify fields for both first and second tables using ->first($fields) and->with(['my_second_table' => function ($query) { $query->select('id', 'some_value'); }]) unless I specify primary key of second table in ->first($fields). How do I work around this?
TL;DR; Issue: http://laravel.io/bin/YyVjd
You can probably use Laravel Eloquent relationship to achieve it.
https://laravel.com/docs/5.2/eloquent-relationships#one-to-many
Or you can remap the returned data to a new response object using $appends.
Try something here,
http://laraveldaily.com/why-use-appends-with-accessors-in-eloquent/
This is just some clues and there is a lots work to do.
FYI, you can set $visible in your model to specify which attributes is visible.
I've got a relatively simple setup running using SilverStripe 3.2.1 with the restfulserver addon and using a variety of widgets which are associated to a Page using the elemental addon.
When I make a GET request via the API to retrieve some of Page #1's data, I can see the associated ElementAreaID:
# GET /api/v1/Page/1.json?fields=Title,URLSegment,Content,ElementArea
{
"Title": "Welcome",
"URLSegment": "home",
"Content": "A bunch of HTML here from all the widgets in the page...",
"ElementArea": {
"className": "ElementalArea",
"href": "http://ss.local:3000/api/v1/ElementalArea/11.json",
"id": "11"
}
}
If I follow the links through the ElementalArea API calls it will list out all of the elements in my page:
# GET /api/v1/ElementalArea/11.json
{
"ID": "11",
"Widgets": [
{
"className": "Widget",
"href": "http://ss.local:3000/api/v1/Widget/9.json",
"id": 9
},
{
"className": "Widget",
"href": "http://ss.local:3000/api/v1/Widget/8.json",
"id": 8
},
...
]
}
And if I follow those API paths it will serve up the contents of the latest version of each of the Widgets.
My question is how can I include certain fields from the Widget DataObjects within the original Page field list?
I'd like ideally to have the Content field from each Widget be returned in an array with the initial Page API request.
For reference:
A Page has one ElementArea
An ElementArea has many Widgets
A Widget contains content that I want for my Page
Preamble: It doesn't seem there's currently a way to output array-like datastructures with the RESTful server module (except for relations of course). The proposed solution is a hack that abuses how JSONDataFormatter formats output.
Since JSONDataFormatter uses forTemplate to render a field before converting it to JSON, we can create our own Object renderer that returns an array instead of a string via forTemplate. This might look like this:
class FlatJSONDataList extends ViewableData
{
protected $list;
public function __construct(array $list)
{
parent::__construct();
$this->list = $list;
}
public function forTemplate()
{
return $this->list;
}
}
Then in your Page, it should be sufficient to have an additional method, like so:
public function getWidgetContents()
{
return FlatJSONDataList::create(
$this->ElementArea()->Widgets()->column('Content')
);
}
Then you can include WidgetContents in your Field-list to get all widget Content fields in an array:
GET /api/v1/Page/1.json?fields=Title,URLSegment,Content,WidgetContents
I've been reading the documentation and other example posts since yesterday to work out how to add sort by field and the direction to my current query array here but so far no luck. I've seen examples like {"sort": {"_score": "desc"}, "query": { .... but couldn't quiet get my head around it to modify my array since this is the very first time I'm dealing with ES.
All I need is: I want to be able to sort by price or stock in asc or desc order.
Everytime I try to update my array to implement sorting, I get {"error":{"code":0,"message":"Invalid option sort"}} error.
NOTE: The query array is being passed to Pagerfanta to get results.
$paginator = $this->productFinder->findPaginated($myArray)
->setMaxPerPage($limit)
->setCurrentPage($page);
Here's an example:
{
"_source":true,
"query":{
"simple_query_string":{
"query":"1*"
}
},
"sort":[
{
"price":{
"order":"desc",
"missing":"_last"
}
},
{
"_score":{
"order":"desc",
"missing":"_last"
}
}
]
}
For the specific field and direction:
{
"sort": {
"price": "asc"
}
... rest of the code
}
For no particular sorting (this is set by default):
{
"sort": {
"_score": "desc"
}
... rest of the code
}