Laravel & PHP: Return special formatted JSON - php

After doing a query, how can I create and echo a formatted JSON like this:
{
"results": [
{
"user_id": "1",
"name": "Apple",
"address": "7538 N LA CHOLLA BLVD",
"city": "Palo Alto",
"state": "CA",
"latlon": [
-111.012654,
32.339807
],
},
{
"user_id": "2",
"name": "Microsoft",
"address": "75 S BWY STE 400",
"city": "Palo Alto",
"state": "CA",
"latlon": [
-73.764497,
41.031858
],
},
],
"meta": {
"page": 1,
"per_page": 10,
"count": 493,
"total_pages": 50
}
}
This is my current query:
public function getAgenciesJson() {
$agencies = DB::table('users')->where('type','a')->orWhere('type','l');
}
Haven't figured out how to output JSON like that, considering I have a "latlon" field like [-111.012654,32.339807], also a "results" tag and a "meta" tag.
Thanks in advance

What you need is something called a transformer (or presenter) to convert your raw model into a format that can be sent to your users.
A very popular package is called Fractal (http://fractal.thephpleague.com/) by Phil Sturgeon. There's a Laravel package, that might make it a bit easier to use, called Larasponse (https://github.com/salebab/larasponse).
Phil actually a blog post about this just the other day - https://philsturgeon.uk/api/2015/05/30/serializing-api-output/ - that goes into why you should always have some kind of transformer between your models and what you send to your users.
There's also a guide about using Larasponse and Fractal that might be of use here - http://laravelista.com/laravel-fractal/.
The gist of it boils down to passing the model through another class that will take the models values and build an array/object in a known/fixed format, e.g. (from Phil's blog post)
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => (int) $book->yr,
'author' => [
'name' => $book->author_name,
'email' => $book->author_email,
],
'links' => [
[
'rel' => 'self',
'uri' => '/books/'.$book->id,
]
]
];
This way you're not exposing your original field names and if at any point your column names should change you only need to update that in 1 place rather than having to get any user of your JSON to update their site/app. It will also allow you to do string manipulation of your latlon column so that you can split it into 2 different values.
Using a slightly modified example from the Fractal documentation. Say you have a transformer for a User
class UserTransformer extends Fractal\TransformerAbstract
{
public function transform(User $user)
{
return [
'id' => (int) $user->id,
'name' => $user->first_name . ' ' . $user->last_name,
];
}
}
You can then use this class to either transform a single item of a collection
$user = User::find(1);
$resource = new Fractal\Resource\Item($user, new UserTransformer);
// Or transform a collection
// $users = User::all();
// $resource = new Fractal\Resource\Collection($users, new UserTransformer);
// Use a manager to convert the data into an array or json
$json = (new League\Fractal\Manager)->createData($resource)->toJson();
Fractal includes a paginator adapter for Laravel that can be used straight away
$paginator = User::paginate();
$users = $paginator->getCollection();
$resource = new Collection($users, new UserTransformer);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));

The method exists:
->toJson()
REF: http://laravel.com/docs/4.2/eloquent#converting-to-arrays-or-json
Update your getAgenciesJson to :
public function getAgenciesJson() {
return DB::table('users')->where('type','a')->orWhere('type','l')->toJson();
}
Then you could echo by:
<?= SomeModel::getAgenciesJson(); ?>
To modify the column names you can update your select. Here is an example taken from the Laravel Docs:
$users = DB::table('users')->select('name as user_name')->get();
Here would be a more fully realized version of what you are looking for minus the column aliases since you didn't really mention what they were.
public function getAgenciesJson($page, $per_page = 10) {
$output = [
'results' => [],
'meta' => [],
];
// Get Results
$output['results'] = DB::table('users')->where('type','a')->orWhere('type','l')->take($per_page)->skip($per_page * ($page - 1))->get();
// Set Meta
$output['meta'] = [
'page' => $page,
'per_page' => $per_page,
'count' => DB::select('SELECT FOUND_ROWS()'),
'total_pages' => DB::table('users')->count() / $per_page
];
// Return
return json_encode($output);
}
Your original code didn't attempt to get or handle the pagination information but this example covers that in order to provide the meta data you indicated you wanted returned.
Not sure if you wanted Count to be the number of the current result set or a count of all the records on that table. If you don't want the current set you but rather the entire count you can use DB::table('users')->count() though I would assign it to a variable and use that rather than calling it twice in the meta info.

public function getAgenciesJson() {
$agencies = DB::table('users')->where('type','a')->orWhere('type','l')->get();
return response()->tojson($agencies,200);//for response with status code 200
}
You should call get function to get data in array format and response function will format your output to json. Second paramater signifies which status code you have to assign for your response.

Related

How to store multiple records via JSON from a Debugger (Lumen, RESTClient)

I'm using RESTClient to send the http request to the backend:
restclient.net
Currently, my JSON looks like this:
[
{"title":"my blogADD","description":"myblogdescriptionADD","status":1},
{"title":"my blogUPDATEDADD","description":"myblogdescriptionUPDATEDADD","status":1},
{"title":"my blog33","description":"myblogdescription33ADD","status":1}
]
The table in the DB looks like this, its name is "articles":
https://imgur.com/onXEgzg
The function inside the controller responsible to insert the data looks like this:
public function create(Request $request){
$this->validate($request, [
'title' => 'required',
'description' => 'required'
]);
//insert record
$article = Article::create($request->all());
return response()->json($article, 201);
}
So far, when I send the request, the data doesn't get inserted.
The header is set to content-type:application/json
inserting a single record works.
Sending the above array of objects doesn't do anything, it doesn't even throw an error.
I already read somewhere that one might use the id as key to identify each object inside the array. But is that really the problem here?
The ID of the table is already the PK and its set to AUTO_INCREMENT, so at least technically assigning an ID manually shouldnt be necessary, although of course the order of the inserted might differ from the order they arrived inside the inputted array.
So what am I missing?
I am pretty new to Lumen, and I'm just learning to use the model.
The controller code stems from a tutorial video:
https://www.youtube.com/watch?v=6Oxfb_HNY0U
I must admit that I don't really understand what the
$request->all()
inside
$article = Article::create($request->all());
does.
When reading the official laravelDOC, I found this syntax to save records to the DB
https://laravel.com/docs/5.8/eloquent
<?php
namespace App\Http\Controllers;
use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class FlightController extends Controller
{
/**
* Create a new flight instance.
*
* #param Request $request
* #return Response
*/
public function store(Request $request)
{
// Validate the request...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
}
}
and it differs quite a lot from what I have, so I wonder how my syntax actually worx xD
EDIT:
Here is the debugging result of $request->all()
^ array:3 [
0 => array:3 [
"title" => "my blogADD"
"description" => "myblogdescriptionADD"
"status" => 1
]
1 => array:3 [
"title" => "my blogUPDATEDADD"
"description" => "myblogdescriptionUPDATEDADD"
"status" => 1
]
2 => array:3 [
"title" => "my blog33"
"description" => "myblogdescription33ADD"
"status" => 1
]
]
See The API:
https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Builder.html#method_create
create is only save a new model and return the instance.
https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_insert
Use insert instead of create
Article::insert($request->all());

the match filter must be an expression in an object with mongo aggregation in PHP

So first here's what I'm trying to do :
I got a datatable with some custom inputs. Users can select skills (php, mysql etc) and the standard text input. Users can select multiple skills. When multiple skills are selected it's supposed to be kind of an AND AND query. So both skills must be available.
The standard text input in datatables would contain information such as the name of a customer, their email or job function.
For example I check PHP and Javascript, in my text search I put Google.
Which would mean I'm looking for a document which has both PHP and Javascript in the tags (Or any of their aliases eg: Javascript => js), and has google in any other field thats listed (Customer, function, sender name, sender email or subject).
But it should also be able to search for documents just matching the skills or only for the text input.
I have been trying to build arrays with all query options and merge those depending on input to the function but so far nothing has been working.
the match filter must be an expression in an object
So below is an example of a document I got in Mongo
{
"_id": {
"$oid": "superfancyID"
},
"parsed": {
"tags": [
"sql",
"php"
],
"customer": "CustomerName",
"function": "functionName",
"mail_properties": {
"senderName": "SenderName",
"senderEmail": "example#example.com",
"subject": "FW: Test email",
}
}
}
Here is some code I got to try and build my query
public function handler(int $skip, int $limit, array $filters, string $filterString = '') {
# $filters are skills
# $filterString is a string of everything else you put in the search field of the datatable
/** #var \MongoDB\Collection $collection */
$collection = $this->emailRepository->getCollection();
/** #var array $regex */
$regex = [];
if(!empty($filterString))
{
$strings = explode(' ', $filterString);
foreach($strings as $item)
{
$regex[] = new Regex($item, 'i');
}
}
#This function basically does the same as above.
#The only change is that this also goes over any aliases for skills. (Javascript => js)
/** #var array $aliasRegex */
$aliasRegex = $this->createAliasRegex($filters);
$skillsFilters = [];
#This is where I attempt to create a part of the query for just the skills.
if(!empty($aliasRegex)) {
$skillsFilters = [
'$and' => [
'$or' => [
['parsed.tags' => [
'$in' => $aliasRegex
]]
]
]
];
}
$stringFilters = [];
#This is where I try and build the rest of the query.
if(!empty($regex)) {
$stringFilters = ['$or' =>
['parsed.function' => [
'$in' => $regex
]],
['parsed.mail_properties.subject' => [
'$in' => $regex
]],
['parsed.customer' => [
'$in' => $regex
]]
];
}
$documents = $collection->aggregate([
[
'$match' => [
array_merge($stringFilters, $aliasFilters)
]
]
,[
'$limit' => $limit,
],[
'$skip' => $skip
]
]);
This is basically all I got, I tried checking my arrays and moving around some parts but nothing has been working for me. I always been getting back the error
the match filter must be an expression in an object

Return only certain data in from a collection - Laravel

I'm learning Laravel and have created a public endpoint where I want to output only certain information of some comments if a user is not authenticated from a GET request.
I have managed to filter out the comments based on whether or not they are approved. I now want to filter out the data that is returned. I have attached a screenshot of what is currently returned.
Ideally, I only want to return the id, name and the body in the json. How can I go about this? I tried the pluck() method which did not give the desired results. Any pointers would be greatly appreciated
public function index(Request $request)
{
if (Auth::guard('api')->check()) {
return Comment::all();
} else {
$comments = Comment::where('approved', 1)->get();
return $comments->pluck('id','name','body');
}
}
To select the particular columns, you can pass columns name to get as
$comments = Comment::where('approved', 1) -> get(['id','name','body']);
You can use a transformer to map the incoming data to a sensible output based on the auth state. The following example comes from the Fractal lib:
<?php
use Acme\Model\Book;
use League\Fractal;
$books = Book::all();
$resource = new Fractal\Resource\Collection($books, function(Book $book) {
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => $book->yr,
'author' => [
'name' => $book->author_name,
'email' => $book->author_email,
],
'links' => [
[
'rel' => 'self',
'uri' => '/books/'.$book->id,
]
]
];
});
Ideally, you would create 2 classes that extend from Transformer and pass the correct one to the output.
If you want to pass the result as json respose
$comments = Comment::where('approved', 1)->pluck('id','name','body')->toArray();
return Response::json($comments);
If you want to pass the result as to blade
$comments = Comment::where('approved', 1)->pluck('id','name','body')->toArray();
return view('your_blade_name')->with('comments',$comments);

add custom value for "type" column in laravel default notification table

I am using laravel's default notification system. The type column of the table gets populated by full class path like "App\Notifications\Upvoted" . I am only filling the data column by myself like this:
public function toDatabase($notifiable)
{
return [
"post" => $this->post,
"user" => Auth::user()
];
}
How can I add custom value for "type" column too.
As I am new to Laravel your help will be greatly appreciated.
You can not do this because the type Field is following morph rules in the Laravel framework.
If you need to save extra data in Notification Table you can pass in an array and then add as a JSON field in data field.
For instance you return:
public function toArray($notifiable)
{
return [
'post_id' => $this->post_id,
'user_id' => Auth::user()->id,
];
}
And the result in notifacation data field will be:
{ "post_id": "2", "user_id": "1" }

POST nested Json with Silex

I'm new in Silex and I'm trying to retrieve nested Json Data with POST. Here is the Json part.
{
"action": "opened",
"number": 3,
"pull_request": {
"id": 35845110,
"number": 3,
"state": "open",
"locked": false,
"title": "Kie"
}
}
My code works for action, number and pull_request but I only want to use the data state in pull_request and I don't know how to collect it.
$app->before(function (Request $request) {
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
$data =json_decode($request->getContent(), true);
$request->request->replace(is_array($data) ? $data : array());
}
});
$app->post('/api', function(Request $request) use ($app) {
$pullRequest = array(
'action' => $request->request->get('action'),
'number' => $request->request->get('number'),
'pull_request' => $request->request->get('pull_request'),
'state' => $request->request->get('state')
);
return $app->json($pullRequest,201);
});
Thanks for your help
It's quite simple actually. After you decode your json object, pull_request becomes an array, and therefore if you want to access state you need to refer to pull_request first. One way would be the so called array dereferencing, like this:
'state' => $request->request->get('pull_request')['state']
but in order to use that syntax, your version of PHP must be at least 5.4.
You can also assign pull_request to a variable and use it like any other array up until now.
$pullRequest = $request->request->get('pull_request');
//....
'state' => $pullRequest['state']

Categories