Laravel Collection Return - laravel

I am trying to figure out why laravel 5.6 when you have a collection it prints out the json response paginated than if you are using a normal return response with json on a paginated list. So I have right now this returning in my collection.
{
"data": [],
"links": {
"first": "http://local/api/v3/x?=1",
"last": "http://local/api/v3/x?=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://local/api/v3/x",
"per_page": "5",
"to": 3,
"total": 3
}
}
And what I want it to look like is:
{
"current_page": 1,
"data": [],
"first_page_url": "http://local/api/v3/x?=1",
"from": null,
"last_page": 1,
"last_page_url": "http://local/api/v3/x?=1",
"next_page_url": null,
"path": "http://local/api/v3/x",
"per_page": 50,
"prev_page_url": null,
"to": null,
"total": 0
}
I have tried to use this following code in the collection and it doesnt work, I have found a few functions to get the data I Want but I am still missing most of the data.
<?php
namespace App\Http\Resources\V3;
use Illuminate\Http\Resources\Json\ResourceCollection;
class DeedTypeCollection extends ResourceCollection
{
public $collects = 'App\Http\Resources\V3\x';
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'current_page' => $this->currentPage(),
'data' => $this->collection,
'first_page_url' => $this->links['first'],
'from' => $this->meta['from'],
'last_page' => $this->meta['last_page'],
'last_page_url' => $this->links['last'],
'next_page_url' => $this->links['next'],
'path' => $this->meta['path'],
'per_page' => $this->meta['per_page'],
'prev_page_url' => $this->links['prev'],
'to' => $this->meta['to'],
'total' => $this->meta['total']
];
}
}
Why does laravel change the default pagination on the collections and how can I fix this to match the default/second dataset.
Here is where the query is built.
/**
* Function: prepareIndexSelect.
*
* #param: $request - Request sent to controller
*
* Description: Returns queried collection.
*/
public static function prepareIndexSelect($request)
{
$model = new self();
$per_page = 50;
$page = 1;
$order_by = $model->getKeyName();
$sort_by = 'asc';
if ($request->has('per_page')) {
$per_page = $request->input('per_page');
}
if ($request->has('sortBy')) {
$order_by = $request->input('sortBy');
}
if ($request->has('sortByAsc')) {
$sort_by = $request->input('sortByAsc');
}
if ($request->has('page')) {
$page = $request->input('page');
}
return $model->when($request->has('fields'), function ($query) use ($request) {
self::prepareSelectColumns($query, $request->input('fields'));
})->when($request->has('includes'), function ($query) use ($request) {
self::prepareIncludedTables($query, $request->input('includes'));
})->whereSearch($request->input('search'))
->orderBy("{$order_by}", "{$sort_by}")
->paginate($per_page, null, null, $page);
}

Looking over the documentation it appears as though you will need to return your derived collection at the respective endpoint.
Your derived resource collection:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class DeedTypeCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request
* #return array
*/
public function toArray($request)
{
return [
'current_page' => $this->currentPage(),
'data' => $this->collection,
'first_page_url' => $this->links['first'],
'from' => $this->meta['from'],
'last_page' => $this->meta['last_page'],
'last_page_url' => $this->links['last'],
'next_page_url' => $this->links['next'],
'path' => $this->meta['path'],
'per_page' => $this->meta['per_page'],
'prev_page_url' => $this->links['prev'],
'to' => $this->meta['to'],
'total' => $this->meta['total']
];
}
}
Your endpoint:
use App\DeedType;
use App\Http\Resources\DeedTypeCollection;
Route::get('/deed-types', function () {
return new DeedTypeCollection(DeedType::all());
});
You may also extend PaginatedResourceResponse this should allow you to override paginationInformation to format the response the way you need it.
protected function paginationInformation($request)
{
return $this->resource->resource->toArray();
}
Then in your derived collection:
public function toResponse($request)
{
return (new MyDerivedPaginatedResourceResponse($this))->toResponse($request)
}

Related

how to change query so it only shows name instead whole array (laravel 8)

I got a query that gets the data into a collection, problem is that it shows foreign id but i want it to display what i have given in the url parameters.
columnsGiven is the parameter from url. contains column names with child: "language.name". so column=active,title,language.name
For example i get this:
"name": "george",
"active": 1,
"language": 1,
and this is what i want:
"name": "george",
"active": 1,
"language": "Dutch",
this is my code:
public function index(Request $request){
$columnsGiven = explode(',', $request->columns);
$tableName = $request->table_name; //example: 'support_guide_translations'
$modelName = $request->model_name; //example: "App\Models\SupportGuideTranslation";
if($request->search){
$query = $modelName::search($request->search);
} else{
$query = $modelName::query();
if($request->sort){
$sort = explode('?', $request->sort);
$query->orderBy($sort[0], $sort[1]);
}
}
foreach ($request->query() as $key => $value) {
// dd($value);
if(!$value == ""){
if(Schema::hasColumn($tableName, $key)){
$query->where($key, $value);
}
if(in_array($key, $columnsGiven)){
dd('true');
}
// $searchWord = Str::contains('account123',$request->search);
}
}
$guides = $query->get();
return GuideResource::collection($guides);
}
}
this is GuideResource, it sends data to vue by making it json first. Not allowed to make changes here, it has to be done in the function index. :
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'active' => $this->active,
'language' => $this->language,
'support_guide' => $this->support_guide,
'support_guide_group' => $this->support_guide_group,
];
}
"language": {
"id": 1,
"name": "Dutch",
"code": "NL",
"created_at": "2021-06-14T10:10:32.000000Z",
"updated_at": "2021-06-14T10:10:32.000000Z"
},
I believe "language" has a relationship with another model.
Isn't this what you are looking for?
return [
// your other attributes...
'language' => $this->language->relationshipName->name,
];
where relationshipName is the method name in the Language model.

Shopify API in PHP using GuzzleHttp array of objects

I am currently attempting to update a bunch of products via the Shopify API, however, when I am sending the request, the product is created, however, it appears to be ignoring things where it is an array or arrays (for example, images or variants).
This is my Shopify Helper class that I am using for all requests.
class Shopify {
protected $api_key;
protected $password;
protected $url;
protected $host;
protected $secret;
protected $client;
public function __construct() {
$this->api_key = env('SHOPIFY_API_KEY');
$this->password = env('SHOPIFY_API_PASSWORD');
$this->secret = env('SHOPIFY_API_SHARED_SECRET');
$this->host = env('SHOPIFY_API_HOST');
$this->url = "https://{$this->api_key}:{$this->password}#{$this->host}";
$this->client = new Client();
}
public function __call($method, $args)
{
$method = strtoupper($method);
$allowedMethods = ['POST','GET','PUT','DELETE'];
if(!in_array($method,$allowedMethods)){
throw new InvalidMethodRequestException();
}
return $this->request($method,trim($args[0]),$args[1] ?? []);
}
protected function request(string $method, string $uri, array $payload)
{
$response = $this->client->request(
$method,
"{$this->url}{$uri}",
[
'form_params' => $payload
]
);
return json_decode($response->getBody());
}
}
Here is an example of me using this client to create a product with a variant:
$shopify = new Shopify();
$result = $shopify->post('/admin/api/2020-10/products.json', [
'product' => [
'title' => $product->title,
'body_html' => $product->body_text,
"variants" => [
[ "sku" => $product->sku, "price" => 20.00 ]
]
]
]);
As I mentioned above, the product is created in Shopify, but is missing anything where the data is an array of arrays. Could this be todo with the way GuzzleHttp encodes the data? How can get this data in a format that Shopify needs?
Your code looks good. The variants output maybe need some conversion to json format.
As you can see here, the expected post request:
"variants": [
{
"option1": "First",
"price": "10.00",
"sku": "123"
},
I would try to dump the post variable before the request and see what is wrong.
I made some successfull code with laravel in the past using Facades, and worked. But it took me some time to make it as shopify needs.
PS: I'm using tokens here https://www.shopify.com/partners/blog/17056443-how-to-generate-a-shopify-api-token
See:
//my endpoint, in your case '/admin/api/2020-10/products.json
$endpoint = config('endpoint');
//registered as a token.
$token = config('token');
$postFields = 'product' => [
'title' => $product->title,
'body_html' => $product->body_text,
"variants" => [
[ "sku" => $product->sku, "price" => 20.00 ]
]
];
//in case of any problems, you can uncomment this line and inspect your request.
//json_encode will help with this
//dd(json_encode($postFields));
return Http::withHeaders([
"content-type" => "application/json",
"Authorization" => "Bearer " . $token
])->post($endpoint, $postFields)->json();

How to post data in from back-end testcase to controller

I am new to php and I am currently trying to make a testcase for an add function I wrote for adding records in the table "project_point" of my database. In this testcase I want to post some test data to that add function and check if the data is set correctly.
Project Point Add function
public function addProjectPoint (Request $request) {
$point = new ProjectPoint();
$location = new Point($request->markerLat, $request->markerLong);
$point->project_id = $request->project_id;
$point->location = $location;
$point->area = $request->area;
$point->name = $request->name;
$point->information = $request->information;
$point->category = $request->category;
$point->save();
}
My test case
public function testCreateProjectPoint()
{
$this->post('admin/projectpoint/create', [
'project_id' => 1,
'markerLat' => 5.287020206451416,
'markerLong' => 51.68828138589033,
'area' => null,
'name' => 'TestCaseProjectPoint',
'information' => 'This is a automated test ProjectPoint, please delete this point!',
'category' => 'bezienswaardigheid'
]);
$this->assertDatabaseHas('interest_point', [
'project_id' => 1,
'location' => new Point(5.287020206451416, 51.68828138589033),
'area' => null,
'name' => 'TestCaseProjectPoint',
'information' => 'This is a automated test ProjectPoint, please delete this point!',
'category' => 'bezienswaardigheid'
]);
/*
$test = factory(ProjectPoint::class)->create();
$this->post('admin/projectpoint/create', $test);
$this->assertDatabaseHas('project_point', $test);
*/
}
ProjectPoint model
class ProjectPoint extends Model
{
use SpatialTrait;
protected $table = 'interest_point';
protected $fillable = ['project_id', 'name', 'information', 'category' ];
protected $spatialFields = [
'location',
'area'
];
public $timestamps = false;
public function project()
{
return $this->belongsTo('App\Models\Project', 'project_id');
}
}
The output of the test is:
Failed asserting that a row in the table [interest_point] matches the attributes {
"project_id": 1,
"location": {
"type": "Point",
"coordinates": [
51.68828138589033,
5.287020206451416
]
},
"area": null,
"name": "TestCaseProjectPoint",
"information": "This is a automated test ProjectPoint, please delete this point!",
"category": "bezienswaardigheid"
}.
But I expect to see the test case succeed and when checking the database no records have been added to the database
Have you tried still putting location and area in your fillables?

Laravel customer API resource with relationships

Controller function:
public function index () {
// TESTED
// The getAllActiveSuppliers() function just return Supplier::pagniate(10)
$suppliers = $this -> model -> getAllActiveSuppliers();
return new SupplierResource($suppliers);
}
Returned Json:
{
"current_page": 1,
"data": [
{
"id": 23,
"name": "Test Name",
"description": "Test Description",
"created_by": {
"id": 1,
"name": "Test 1",
"email": "admin#admin.com",
"email_verified_at": null,
"active": 1,
"created_at": "2018-10-12 14:17:38",
"updated_at": "2018-10-12 14:17:38"
},
"updated_by": {
"id": 1,
"name": "Test 1",
"email": "admin#admin.com",
"email_verified_at": null,
"active": 1,
"created_at": "2018-10-12 14:17:38",
"updated_at": "2018-10-12 14:17:38"
},
"deleted_at": null,
"created_at": "2018-10-31 01:46:11",
"updated_at": "2018-11-02 22:05:14",
}
],
...
}
What I am trying to do:
In the created_by and updated_by I just want to show name, email nothing else.
What I have tried to do:
I have tried to create an API resource collection
Supplier.php API Resource Collection :
public function toArray($request)
{
return parent::toArray($request);
}
For anyone that may still need this as I was searching for myself in this topic
First create a resource for your relation with name, email fields
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'name' => $this->name,
'email' => $this->email,
];
}
}
Then create SupplierResource with relation created_by to User
class SupplierResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'created_by' => new UserResource($this->created_by)
];
}
}
In the Controller
public function index () {
$suppliers = $this->model->getAllActiveSuppliers();
return SupplierResource::collection($suppliers);
}
You first need to define a structure for a singular JsonResource object:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
...
'created_by' => $this->created_by->only('name', 'email'),
...
];
}
And then tell your ResourceCollection to use the class:
Customizing The Underlying Resource Class
Typically, the
$this->collection property of a resource collection is automatically
populated with the result of mapping each item of the collection to
its singular resource class. The singular resource class is assumed to
be the collection's class name without the trailing Collection
string.
For example, UserCollection will attempt to map the given user
instances into the User resource. To customize this behavior, you may
override the $collects property of your resource collection
(From https://laravel.com/docs/5.7/eloquent-resources#concept-overview)
If your ResourceCollection doesn't do anything extra, you might not need it. Every JsonResource can transform itself for a collection with the collection() method, e.g. JsonResource::collection($models)
Think we can add item to parent::toArray($request)
public function toArray($request)
{
$array = parent::toArray($request);
$array['created_by'] = $this->created_by->only('name', 'email');
return $array;
}

How to create different Magento categories for main and mobile store view on one domain name

I am looking solutions for using different categories for main and mobile Magento store view. I have configure mobile store view as new theme with user agent string and exception.
How can I do show one categories on main store view and another one categories on mobile store view. Both store views use by one domain name.
What I will suggest it create an category attribute with dropdown. Following Script will help you to do so :
SQL Setup file :
<?php
$installer = $this;
$installer->startSetup();
$installer->addAttribute("catalog_category", "wheretoshow", array(
"type" => "int",
"backend" => "",
"frontend" => "",
"label" => "Where to Show",
"input" => "select",
"class" => "",
"source" => "modulename/eav_entity_attribute_source_categoryoptions",
"global" => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
"visible" => true,
"required" => false,
"user_defined" => false,
"default" => "Main Website",
"searchable" => false,
"filterable" => false,
"comparable" => false,
"visible_on_front" => false,
"unique" => false,
"note" => ""
));
$installer->endSetup();
Model/Categoryoptions.php
<?php
class class Packagename_Modulename_Model_Eav_Entity_Attribute_Source_Categoryoptions extends Mage_Eav_Model_Entity_Attribute_Source_Abstract
{
/**
* Retrieve all options array
*
* #return array
*/
public function getAllOptions()
{
if (is_null($this->_options)) {
$this->_options = array(
array(
"label" => Mage::helper("eav")->__("Mobile Website"),
"value" => 1
),
array(
"label" => Mage::helper("eav")->__("Main Website"),
"value" => 2
),
);
}
return $this->_options;
}
/**
* Retrieve option array
*
* #return array
*/
public function getOptionArray()
{
$_options = array();
foreach ($this->getAllOptions() as $option) {
$_options[$option["value"]] = $option["label"];
}
return $_options;
}
/**
* Get a text for option value
*
* #param string|integer $value
* #return string
*/
public function getOptionText($value)
{
$options = $this->getAllOptions();
foreach ($options as $option) {
if ($option["value"] == $value) {
return $option["label"];
}
}
return false;
}
/**
* Retrieve Column(s) for Flat
*
* #return array
*/
public function getFlatColums()
{
$columns = array();
$columns[$this->getAttribute()->getAttributeCode()] = array(
"type" => "tinyint(1)",
"unsigned" => false,
"is_null" => true,
"default" => null,
"extra" => null
);
return $columns;
}
/**
* Retrieve Indexes(s) for Flat
*
* #return array
*/
public function getFlatIndexes()
{
$indexes = array();
$index = "IDX_" . strtoupper($this->getAttribute()->getAttributeCode());
$indexes[$index] = array(
"type" => "index",
"fields" => array($this->getAttribute()->getAttributeCode())
);
return $indexes;
}
/**
* Retrieve Select For Flat Attribute update
*
* #param int $store
* #return Varien_Db_Select|null
*/
public function getFlatUpdateSelect($store)
{
return Mage::getResourceModel("eav/entity_attribute")
->getFlatUpdateSelect($this->getAttribute(), $store);
}
}
While Fetching the categories on frontend, filter those by this attribute depending on your website.

Resources