How to specify additionalProperties with l5-swagger - laravel

I am trying to write OpenAPI documentation for a project (written with Laravel) and struggling with several API points.
One returns
{
"active_options": {
"1": {
"name": "name1",
"type": "type1"
},
"2": {
"name": "name2",
"type": "type2"
},
"3": {
"name": "name3",
"type": "type3"
}
},
"server": {
"url": "URL...",
"settings1": "value"
},
"message": "Server settings retrieved."
}
I am struggling with how to write this annotation with l5-swagger plugin.
The "1", "2", "3" are optional and any combination of them is valid.
I wanted to use optionalProperties, but I don't know how to combine them together.
This is the closest I got:
* #OA\Response(
* response=200,
* description="Settings",
* #OA\JsonContent(
* #OA\Property(property="options",
* #OA\Items(
* #OA\Property(property="name", type="string"),
* #OA\Property(property="type", type="string")
* )
* ),
* )
* ),
But the sample generates this:
{
"options": [
{
"name": "string",
"type": "string"
}
]
}
Which obviously is missing the "1": ... part.
Maybe a better question would be how to do unnamed properties?

This is my solution:
/**
*
* #OA\Schema(
* schema="ExampleResponse",
* #OA\Xml(name="ExampleResponse"),
* #OA\Property(property="message", type="string", example="My solution."),
* #OA\Property(property="errors", type="object",
* #OA\AdditionalProperties(type="array",
* #OA\Items(
* #OA\Property(property="name", type="string"),
* #OA\Property(property="type", type="string")
* )
* )
* ),
* )
*
*/

Related

Laravel Eloquent - Accessor not working with relations

I have two model Post and PostImage. In my PostImage model I have set accessor that will concat the image URL to image. I am getting my image response but URL is not prefixed.
Here my PostImage model
class PostImage extends Model
{
use HasFactory;
protected $primaryKey = 'post_image_id';
/**
* Get the image
*
* #return \Illuminate\Database\Eloquent\Casts\Attribute
*/
protected function name(): Attribute
{
return Attribute::make(
get: fn ($value) => config('custom.filepath.products.resize128') . $value,
);
}
}
I am calling return Post::with('images')->get() and here is the response -
{
"post_id": 4,
"customer_id": 1,
"title": "a",
"description": "Its a description",
"target_price": 3000,
"date_of_availability": "2023-02-04",
"condition": "abc",
"latitude": "-31.95303000",
"longitude": "115.85360000",
"attributes": "{\"list\":[{\"title\":\"Color\",\"value\":\"Red1\"},{\"title\":\"Frame\",\"value\":\"Metal Frame1\"}]}",
"created_at": "2023-02-06T08:01:42.000000Z",
"updated_at": "2023-02-06T08:01:42.000000Z",
"images": [
{
"post_image_id": 1,
"post_id": 4,
"name": "167567052363e0b3fbc2fe9.jpg",
"created_at": "2023-02-06T08:02:06.000000Z",
"updated_at": "2023-02-06T08:02:06.000000Z"
},
{
"post_image_id": 2,
"post_id": 4,
"name": "167567052663e0b3fe46bf8.jpg",
"created_at": "2023-02-06T08:02:06.000000Z",
"updated_at": "2023-02-06T08:02:06.000000Z"
}
]
}
I was missing use Illuminate\Database\Eloquent\Casts\Attribute;

How to get all (parent) data and load their children data (eager load) but with condition on parent column?

let say I want to get all users and their posts only if user.status is true.
How to achieve this using eager loading (i.e with)
data I wanted to get:
{
"users": [
{
"id": 1,
"status": false
},
{
"id": 2,
"status": false
},
{
"id": 3,
"status": true,
"posts": [
{
"id": 1,
"comment": "abc"
},
{
"id": 2,
"comment": "abc"
},
{
"id": 3,
"comment": "abc"
}
]
}
]
}
You can easily achieve this by loading all users, then filtering the ones you want to load, and finally eager load their relationship.
$users = User::all();
$users->filter->status->load('posts');
If you are using a fairly modern version of Laravel, you may want to look at the documentation on API Resources:
https://laravel.com/docs/7.x/eloquent-resources
Conditional Relationships docs
Try something like this:
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
...
'posts' => $this->when((bool) $this->status, PostResource::collection($this->whenLoaded('posts'))),
...
];
}

laravel eloquent relationship

I have two tables
products:id, code,details,created_at
price_revision:id,product_id,revised_price,user,remark,created_at,updated_at
i want to produce following json:
{
"accessory_price":{[
"date":"2015-03-01",
"products":
{[ "product_id":1, "product_name":"test accessory 1", "revised_price":50.00, "user":"samyak", "remarks":"n/a" ], [ "product_id":2, "product_name":"test accessory 2", "revised_price":60.00, "user":"samyak", "remarks":"n/a" ], [ "product_id":3, "product_name":"test accessory 3", "revised_price":70.00, "user":"samyak", "remarks":"n/a" ]}
],
[
"updated_at":"2015-02-01",
"products":
{[ "product_id":1, "product_name":"test accessory 1", "revised_price":40.00, "user":"sam", "remarks":"n/a" ], [ "product_id":2, "product_name":"test accessory 2", "revised_price":50.00, "user":"sam", "remarks":"n/a" ], [ "product_id":3, "product_name":"test accessory 3", "revised_price":60.00, "user":"sam", "remarks":"n/a" ]}
]
]}
I have made model like this:
ProductModel
public function productpricerevision(){
return $this->hasMany('Modules\Quotes\Entities\ProductPriceRevision','product_id');
}
Repository code
public function getProductPrice(){
$productprice=ProductModel::with('productpricerevision')->get();
$data=array('accessory_price'=>$productprice);
return $data;
}
But with this code I get
{
"product_price": [
{
"id": 1,
"code": "G4007",
"detail": "Flyscreen Gasket -4007",
"created_at": "2014-07-11 13:53:02",
"productprice": [
{
"id": 1,
"product_id": 1,
"revised_price": 78,
"user": "sam",
"remark": "test",
"created_at": "2016-03-30 00:00:00",
"updated_at": "2016-03-30 00:00:00"
}
]
},
{
"id": 2,
"code": "CZBR-100S",
"detail": "Single Bearing Roller BR-100-S",
"created_date": "2014-07-11 13:57:15",
"productprice": [
{
"id": 4,
"product_id": 2,
"revised_price": null,
"user": sam,
"remark": null,
"created_at": "2016-03-30 00:00:00",
"updated_at": "2016-03-30 00:00:00"
}
]
},
Product Migration
class CreateProductsTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('products', function(Blueprint $table)
{
$table->integer('id');
$table->string('code', 25);
$table->text('detail', 65535)->nullable();
$table->timestamp('created_date')->default(DB::raw('CURRENT_TIMESTAMP'));
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('products');
}
}
price_revision
public function up()
{
Schema::create('product_price_revision', function(Blueprint $table)
{
$table->increments('id');
$table->integer('product_id');
$table->double('revised_price')->nullable();
$table->string('user')->nullable();
$table->text('remark')->nullable();
$table->foreign('product_id')->references('id')->on('products')->onDelete('CASCADE')->onUpdate('CASCADE');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('product_price_revision');
}
I have tried several other methods too but at the end i got lost. Any help is appreciated.

icCube - set serie color for 2 dimensions in the columns in a amCharts combo chart

I have got the following dynamic chart (see attached picture). For the sake of reproducability I have created a very simple chart based on the standard icCube's sales model.
The idea is to have two facts on the column axes with the data for the selected years. To distinguish between the facts I want to assign a color to the facts that fades over the years. When you add years, the new year will get the color that belongs to the fact, but a little bit lighter. The screenprints below will clarify.
My question is: is there a way to do this with amCharts/combo in relation with the MDX/setting of icCube?
In the attachments you see two graphs:
but when I de-select 2008, this happens:
I have included the JSON so you can reproduce this in the Sales demo on a plain vanilla icCUbe 5.1.2 installation.
{
"classID": "ic3.ReportGuts",
"guts_": {
"ic3Version": 12,
"schemaName": "Sales",
"cubeName": "Sales",
"layout": {
"classID": "ic3.FixedLayout",
"guts_": {
"ic3Version": 12,
"grid": 10,
"boxes": [
{
"classID": "ic3.FixedLayoutBox",
"guts_": {
"ic3Version": 12,
"header": "chart with color serie that should match the selected fact (orange = Amount, blue = Amount_2)",
"behaviour": "Fixed Box",
"noPrint": false,
"position": {
"top": 100,
"left": 0,
"width": 1021,
"height": 421
},
"widgetAdapterUid": "w12",
"zIndex": 2008
}
},
{
"classID": "ic3.FixedLayoutBox",
"guts_": {
"ic3Version": 12,
"header": "select years",
"behaviour": "Fixed Box",
"noPrint": false,
"position": {
"top": 0,
"left": 60,
"width": 630,
"height": 80
},
"widgetAdapterUid": "w14",
"zIndex": 2009
}
}
],
"theme": "Classic",
"sizeIndicator": {
"width": 1024,
"height": 708
}
}
},
"widgetMgr": {
"classID": "ic3.WidgetAdapterContainerMgr",
"guts_": {
"ic3Version": 12,
"items": [
{
"classID": "ic3.AmchartsComboAdapter",
"guts_": {
"ic3Version": 12,
"navigationGuts": {
"classID": "ic3.AmChartsNavigationStrategy",
"guts_": {
"ic3Version": 12,
"menuVisibility": {
"back": true,
"axisXChange": "All",
"axisYChange": "All",
"filter": "All",
"reset": true,
"widget": true,
"others": "All"
},
"maxAxisMemberCount": 10,
"selectionMode": "disabled",
"drillDownStrategy": {
"type": "mdxExpression",
"options": {
"drd_mdx": "order(nonempty($member.children, #{selMeasure}), #{selMeasure}, BDESC)"
}
}
}
},
"ic3_name": "widget-1",
"ic3_uid": "w12",
"ic3_eventMapper": {
"classID": "ic3.EventWidgetMapper",
"guts_": {
"__ic3_widgetEventsDescription": {},
"fireOnRefresh": {
"name": "All Events (default)",
"uniqueName": "all",
"parentRowId": -1,
"type": "OT"
},
"ic3queryRefresh": "selDimensie"
}
},
"ic3_mdxBuilderUid": "m6",
"__ic3_widgetTypeName": "amCharts/Combo",
"ic3extraOptions": ":{ \"categoryAxis\": {\n \"gridPosition\": \"middle\",\n \"gridAlpha\": 0,\n \"labelFunction\": function(valueText, serialDataItem, categoryAxis) {\n return valueText.substr(0,10).replace(/\\s+/g, '<br>');\n }\n }\n\n }",
"graphs": [
{
"lineThickness": 3,
"balloonText": "[[category]]<br>[[title]]: <b>[[fValue]]</b>"
}
],
"ic3ChartType": [
"column",
"column"
],
"legendPosition": "bottom"
}
},
{
"classID": "ic3.QueryFilterButtonListAdapter",
"guts_": {
"ic3Version": 12,
"ic3_name": "widget-0",
"ic3_uid": "w14",
"ic3_eventMapper": {
"classID": "ic3.EventWidgetMapper",
"guts_": {
"ic3selection": "selYear",
"__ic3_widgetEventsDescription": {}
}
},
"ic3_mdxBuilderUid": "m1",
"__ic3_widgetTypeName": "icCube/Buttons",
"selectionMode": "multi",
"layout": "horizontal"
}
}
]
}
},
"constantMgr": {
"classID": "ic3.ConstantsMgr",
"guts_": {
"constant": []
}
},
"cssMgr": {
"classID": "ic3.CssMgr",
"guts_": {}
},
"javascriptMgr": {
"classID": "ic3.ReportJavascriptMgr",
"guts_": {
"ic3Version": 12,
"js": "/** \n * A function called each time an event is generated. \n * \n * #param context the same object is passed between consumeEvent calls. \n * Can be used to store information. \n * { \n * $report : jQuery context of the report container \n * fireEvent : a function( name, value ) triggering an event \n * } \n * \n * #param event the event information \n * \n { \n * name : as specified in the 'Events' tab \n * value : (optional) actual event value \n * type : (optional) e.g., ic3selection \n * } \n * \n * Check the 'Report Event Names' menu for the list of available events. \n */ \n/* \nfunction consumeEvent( context, event ) { \n if (event.name == 'ic3-report-init') { \n // add your code here \n } \n} \n*/ \n"
}
},
"calcMeasureMgr": {
"classID": "ic3.CalcMeasureMgr",
"guts_": {
"measures": [
{
"name": "Amount2",
"expression": "1.1*[amount]",
"properties": null
}
]
}
},
"mdxQueriesMgr": {
"classID": "ic3.MdxQueriesContainerMgr",
"guts_": {
"mdxQueries": {
"classID": "ic3.BaseContainerMgr",
"guts_": {
"ic3Version": 12,
"items": [
{
"classID": "ic3.QueryBuilderWidget",
"guts_": {
"mdxWizard": {
"classID": "ic3.QueryBuilderWizardForm",
"guts_": {
"rows": [
{
"classID": "ic3.QueryBuilderHierarchyForm",
"guts_": {
"hierarchy": {
"name": "Product",
"uniqueName": "[Product].[Product]"
},
"type": "membersOfLevel",
"membersOfLevel": "[Product].[Product].[Article]"
}
}
],
"cols": [
{
"classID": "ic3.QueryBuilderMeasuresFormBase",
"guts_": {
"measures": [
{
"name": "Amount",
"uniqueName": "[Measures].[Amount]",
"type": "ME"
},
{
"name": "Amount2",
"uniqueName": "[Measures].[Amount2]",
"type": "RCME"
}
]
}
},
{
"classID": "ic3.QueryBuilderEventsFilter",
"guts_": {
"mdxStatement": "#{selYear}"
}
}
],
"filters": [],
"nonEmptyOnRows": false,
"nonEmptyOnColumns": false
}
},
"mdxFlat": {
"classID": "ic3.QueryBuilderFlatMdxForm",
"guts_": {
"useMdxStatement": false
}
},
"ic3_name": "mdx Query-0",
"ic3_uid": "m6"
}
}
]
}
},
"mdxFilter": {
"classID": "ic3.BaseContainerMgr",
"guts_": {
"ic3Version": 12,
"items": [
{
"classID": "ic3.NewFilterQueryBuilderForm",
"guts_": {
"hierarchy": {
"name": "Year",
"uniqueName": "[Time].[Year]"
},
"type": "membersOfLevel",
"membersOfLevel": "[Time].[Year].[Year]",
"defaultsMdx": "2008,2009,2010",
"addDefaultMember": false,
"useAllMember": false,
"useDefaultMemberIfEmpty": false,
"ic3_name": "mdx Filter-0",
"ic3_uid": "m1"
}
}
]
}
},
"actionBuilders": {
"classID": "ic3.BaseContainerMgr",
"guts_": {
"ic3Version": 12,
"items": []
}
}
}
}
}
}
It's not possible in the current version of reporting ( 5.1.2 ). What we're looking here is to use different palettes depending on the selection.
You have to create a new graph for every column to specify the valueField and the color for it. But the number of columns are changing, so we don't know how many graph should be defined.
Having read that there is no answer, yet. I have created the following work-around:
I fix the total years displayed to 4 years in total
In the MDX statement I
This is the MDX statement I use:
WITH
MEMBER [Time].[Year].[All-M].[_1] as null
MEMBER [Time].[Year].[All-M].[_2] as null
MEMBER [Time].[Year].[All-M].[_3] as null
MEMBER [Measures].[Amount2] AS 1.1*[amount]
SET [selection] as order( #{selYear},[Time].[Year].currentmember.key, bdesc) +
[Time].[Year].[All-M].[_1] + [Time].[Year].[All-M].[_2] + [Time].[Year].[All-M].[_3]
SELECT {[measures].[Amount]} * [Time].[Year].[All-M].[_1]
+ item( ({[measures].[Amount]} * {[selection]}),3)
+ item( ({[measures].[Amount]} * {[selection]}),2)
+ item( ({[measures].[Amount]} * {[selection]}),1)
+ item( ({[measures].[Amount]} * {[selection]}),0)
+ item( ({[measures].[Amount2]} * {[selection]}),3)
+ item( ({[measures].[Amount2]} * {[selection]}),2)
+ item( ({[measures].[Amount2]} * {[selection]}),1)
+ item( ({[measures].[Amount2]} * {[selection]}),0)
ON COLUMNS,
{ [Product].[Product].[Article].allmembers } ON ROWS
FROM [Sales]
I do not know why I have to select the 1st tuple (amount * _1), but if I do not select it I get a MDX error.
Nevertheless, I now obtain a perfectly tuned chart:
Hope it might be of help to others.
In the current version you can use for the column colors either the theme palette or the color defined at MDX level in the member ( #Ic3Color). To activate the second option you set the flag 'Use MDX Member Colors' in the report.
On your example it's more a cell property as for setting dynamically colors in tables ( see example ). Sorry, currently there is no easy way to bind the property of the cell with the column fill color of the chart.

Eloquent hasmany relationship issue

I am building an API with the lumen micro-framework using Eloquent for the database.
When i use this code to get the shoppinglists from a group, everything works
$group = Group::with(['shoppingLists' => function($query) {
}])->get();
It returns the following object
{
"id": "797799c2-6044-4a3a-a3a6-a71fbb17de68",
"name": "'t snackske",
"description": "best group ever",
"user_id": "7e74223a-ea06-46bf-ab1a-abb01b287e32",
"created_at": "2015-08-09 20:06:40",
"updated_at": "2015-08-09 20:06:40",
"shopping_lists": [
{
"id": "2423eb3c-7dab-4672-b382-895788dec6a0",
"name": "shop",
"description": "food",
"user_id": "7e74223a-ea06-46bf-ab1a-abb01b287e32",
"group_id": "797799c2-6044-4a3a-a3a6-a71fbb17de68",
"created_at": "2015-08-09 20:06:40",
"updated_at": "2015-08-09 20:06:40"
}
]
}
And when I check the queries that are logged i get the following queries:
{
"query": "select * from `groups`",
"bindings": [],
"time": 0.31
},
{
"query": "select * from `shopping_lists` where `shopping_lists`.`group_id` in (?)",
"bindings": [
"797799c2-6044-4a3a-a3a6-a71fbb17de68"
],
"time": 0.34
}
But when i try select specific fields for the shoppinglists this query returns no records
The query builder:
$group = Group::with(['shoppingLists' => function($query) {
$query->addSelect('id', 'name', 'description');
}])->get();
The response:
{
"id": "797799c2-6044-4a3a-a3a6-a71fbb17de68",
"name": "Group",
"description": "testgroup",
"user_id": "7e74223a-ea06-46bf-ab1a-abb01b287e32",
"created_at": "2015-08-09 20:06:40",
"updated_at": "2015-08-09 20:06:40",
"shopping_lists": []
}
The queries from the getQueryLog function
{
"query": "select * from `groups`",
"bindings": [],
"time": 0.23
},
{
"query": "select `id`, `name`, `description` from `shopping_lists` where `shopping_lists`.`group_id` in (?)",
"bindings": [
"797799c2-6044-4a3a-a3a6-a71fbb17de68"
],
"time": 0.32
}
When i copy and paste the last query from the query log in mysql i get the correct row. For some reason, eloquent doesn't show the data from the shopping_lists.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Group extends Model
{
protected $increments = false;
protected $fillable = [
'id',
'name',
'description',
'user_id'
];
protected $primaryKey = 'id';
/**
* Get shoppinglists from this group
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function shoppingLists()
{
return $this->hasMany('App\ShoppingList', 'group_id', 'id');
}
}
You may have to add the foreign key column to the addSelect() method for Laravel to do it's magic.
Group::with(['shoppingLists' => function($query) {
$query->addSelect('id', 'group_id', 'name', 'description');
}])->get();

Resources