i have 2 objects that i use merge on them like below :
$data = $city->merge($accommodation);
return AccCitySearchResource::collection($data);
and in my resource
return [
'id' => $this->id,
'name' => $this->name,
'english_name' => $this->english_name,
'accommodation_type_id' =>$this->accommodation_type_id,
'grade_stars' =>$this->grade_stars,
'hits' =>$this->hits,
'accommodation_count' => $this->accommodation_count,
];
now all i want is that my $data first shows all cities and then shows all accommodations but now it shows all together is there any way to sort them ??
EDIT
This is what i get now
{
id: 5,
name: "hotel",
accommodation_type_id: 1,
grade_stars: 4,
hits: 22,
accommodation_count: null
},
{
id: 7,
name: "city",
accommodation_type_id: null,
grade_stars: null,
hits: null,
accommodation_count: 0
},
{
id: 10,
name: "hotel",
accommodation_type_id: 1,
grade_stars: 2,
hits: 0,
accommodation_count: null
},
And this is what i want
{
id: 5,
name: "hotel",
accommodation_type_id: 1,
grade_stars: 4,
hits: 22,
accommodation_count: null
},
{
id: 10,
name: "hotel",
accommodation_type_id: 1,
grade_stars: 2,
hits: 0,
accommodation_count: null
},
{
id: 7,
name: "city",
accommodation_type_id: null,
grade_stars: null,
hits: null,
accommodation_count: 0
},
just to mention as you see all hotels comes first and then all cities come .
The recommended way in the documentation is to create your own resource collection if you need to customise the default behavior:
php artisan make:resource AccCitySearchCollection
Then you can customise your collection:
class AccCitySearchCollection{
public function toArray($request)
{
return [
'data' => $this->collection->sortBy('name')
];
}
}
You can then return a collection instance:
$data = $city->merge($accommodation);
return new AccCitySearchCollection($data);
You can use sortBy Collection method
Created Resource Collection for model.
Then in toArray method write:
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'data' => $this->collection->sortByDesc(function($d){
return (int) ($d['name'] == 'hotel');
}),
];
}
This will sort your data as you want.
Hope this answer helps you
Maybe something like this ... But if you are getting your data from a database , maybe you could join and order the query instead..
$data = $city->merge($accommodation)->sortByDesc('some field of first object here');
Related
When i tried to change the value of id to encrypted Id is using map function and transform function, it get zero value no string value or alpha numeric getting replaced .
but it is mandatory to encrypt all id's for API
Please Help me .
Function
function getLatestArticle($profileId)
{
$data = Article::wherehas('articleTrans',function($query){
$query->where('status', ApiConstants::PUBLISHED_STATUS);
})->with(['articleTrans'=>function($q){
$q->select('id','article_id','language_id','title','short_description','description','main_image');
$q->where('status', ApiConstants::PUBLISHED_STATUS);
}])->latest()->paginate(ApiConstants::D_PAGE_C);
$data->getCollection()->transform(function ($value) {
$value->id = encrypt($value->id);
return $value;
});
return $data;
}
Collection
"latest_data": [
{
"id": 0,
"profile_id": 3,
"name": "Test",
"trending": 0,
"new_arrived": 0,
"featured": 0,
"reading_time": null,
"has_series": 0,
"status": 0,
"created_by": 1,
"updated_by": 1,
"deleted_at": null,
"created_at": "2022-03-24T10:27:16.000000Z",
"updated_at": "2022-03-31T11:41:14.000000Z",
"created_from": 1,
"article_trans": {
"id": 8,
"article_id": 12,
"language_id": 1,
"title": "Test",
"short_description": "Test",
"description": "<p><strong>TestTestTestTestTestTestTest </strong></p>\r\n\r\n<p><strong>TestTestTestTestTestTestTestTestTestTestTestTestTest</strong></p>",
"main_image": "1648117636_AXwwVY6JSmNTxmXIiRqGlXiePTl70chCkmMDlehp.jpeg",
"image_url": "http://127.0.0.1:8000/storage/admin/article/image/1648117636_AXwwVY6JSmNTxmXIiRqGlXiePTl70chCkmMDlehp.jpeg"
}
}
Its not good solution to temporarily modify entity objects only for change API response format.
Better solution is using DTO/Transformer classes. Good example of transformers implementation is https://fractal.thephpleague.com/transformers/
In this way you can split presentation and model.
Using any packages is optional, you can write simple transformer classes like this:
// app/Transformers/ArticleTransformer.php
final class ArticleTransformer
{
public static function transform(Article $article): array
{
return [
'id' => encrypt($article->id),
// any fields
];
/*
// or if you need change only one property
return array_replace(
$article->toArray(), // or $article->jsonSerialize()
[
'id' => encrypt($article->id),
],
);
*/
}
public static function transformCollection(Collection $articles): Collection
{
return $collection->map(fn (Article $article): array => self::transform($article));
}
}
Then use this transformer:
return [
'latest_data' => ArticleTransformer::transformCollection($latestArticles),
];
Not very good, but should work:
Override jsonSerialize/toArray method of model, in returning modify your id.
I am getting objects response like this:
"facilities": [
[
{
"facility_id": 1,
"speciality_id": null,
"is_facility_supervisor": 0,
"priv_key": "can_add_doctor,can_view_doctor"
}
],
{
"name": "Patient",
"role_id": 7
}
]
I want name and role_id into facilities and i want expected output like this
"facilities": [
[
{
"facility_id": 1,
"speciality_id": null,
"is_facility_supervisor": 0,
"priv_key": "can_add_doctor,can_view_doctor",
"name": "Patient",
"role_id": 7
}
],
]
How i can achieve this output name and role_id is in separate object but i want in one object like i shared.
My Code:
$specialities = DB::table('user_facility')
->select('user_facility.facility_id','user_facility.speciality_id','user_facility.is_facility_supervisor','user_facility.priv_key')
->where('user_id',$currentUser->id)->get();
$roles = DB::table('roles')
->join('user_facility', 'roles.id', 'user_facility.role_id')
->where('user_facility.user_id', Auth::user()->id)
->select('roles.name','user_facility.role_id')->first();
$superadmin = $is_super_admin->is_super_admin;
$specialities = (object) $speciality_id;
$response = ['is_super_admin' => $superadmin, 'facilities' => array_merge([$specialities,$roles])];
Your help will be highly appreciated?
How about:
$specialitiesAndRoles = DB::table('roles')
->join('user_facility', 'roles.id', 'user_facility.role_id')
->where('user_facility.user_id', Auth::user()->id)
->select('user_facility.facility_id','user_facility.speciality_id','user_facility.is_facility_supervisor','user_facility.priv_key','roles.name','user_facility.role_id')
->get();
When you are anyway joining both tables. Just select everything from the joined table. Please check/add if you have any more where clauses.
I got a table encuesta that has a relationship encuesta_pregunta, the table encuesta_pregunta has a relationship to encuesta_respuesta, it return this
"id": 4,
//...table info...
"encuesta_preguntas": [
{
"id": 10,
"encuesta_id": 4,
"pregunta_id": 5,
//...table info....
"encuesta_respuestas": [
//this relationship can be empty
]
},
{
"id": 11,
"encuesta_id": 4,
"pregunta_id": 3,
//...table info....
"encuesta_respuestas": [
]
},
{
"id": 12,
"encuesta_id": 4,
"pregunta_id": 2,
//...table info....
"encuesta_respuestas": [
]
}
]
}
is there a way to checheck from without looping throught each encuesta_preguntas to know if encuesta_respuesta is empty?
o get the response above like this
$encuesta = Encuesta::with(['encuestaPreguntas' => function($preguntas) {
$preguntas->with(['encuestaRespuestas' => function($respuestas) {
$respuestas->where('user_id','=',auth()->id());
}]);
}])->where('fecha_inicio','<=',date('Y-m-d'))
->where('fecha_fin','>',date('Y-m-d'))
->first();
First. You can do it with Laravel accesors
public function getHasEncuestasAttribute()
{
return $this->encuesta_pregunta != [];
}
When you do $model->has_encuestas it will become true if the relationship isn't empty.
Second. Using relationship method has or doesntHave. If you want to get all the encuesta that have empty relationship you can call Encuesta::doesntHave('encuesta_pregunta') or the opposite that dont have empty relationship Encuesta::has('encuesta_pregunta').
I am working on a project which involves passing data "profiles" via JSON to a web application. I am using Cakephp 3.0 and am very new to it. I store the profiles in a mysql database and can easily query for the data and put it into a basic JSON format with each row being a separate value in the JSON:
Controller.php:
....
public function getProfileData()
{
$uid = $this->Auth->user('id');
$this->loadComponent('RequestHandler');
$this->set('profile', $this->MapDisplay->find(
'all',
['conditions' =>
['MapDisplay.user_id =' => $uid]
]
)
);
$this->set('_serialize', ['profile']);
}
....
get_profile_data.ctp:
<?= json_encode($profile); ?>
Which returns something like this:
{
"profile": [
{
"alert_id": 1,
"alert_name": "Test",
"user_id": 85,
"initialized_time": "2017-03-24T00:00:00",
"forecasted_time": "2017-03-24T00:10:00",
"minimum_dbz_forecast": 0,
"maximum_dbz_forecast": 10,
"average_dbz_forecast": 5,
"confidence_in_forecast": 0.99,
"alert_lat": 44.3876,
"alert_lon": -68.2039
},
{
"alert_id": 1,
"alert_name": "Test",
"user_id": 85,
"initialized_time": "2017-03-24T00:00:00",
"forecasted_time": "2017-03-24T00:20:00",
"minimum_dbz_forecast": 5,
"maximum_dbz_forecast": 15,
"average_dbz_forecast": 10,
"confidence_in_forecast": 0.99,
"alert_lat": 44.3876,
"alert_lon": -68.2039
},
{
"alert_id": 2,
"alert_name": "Test2",
"user_id": 85,
"initialized_time": "2017-03-24T00:00:00",
"forecasted_time": "2017-03-24T00:10:00",
"minimum_dbz_forecast": 10,
"maximum_dbz_forecast": 20,
"average_dbz_forecast": 15,
"confidence_in_forecast": 0.99,
"alert_lat": 44.5876,
"alert_lon": -68.1039
},
{
"alert_id": 2,
"alert_name": "Test2",
"user_id": 85,
"initialized_time": "2017-03-24T00:00:00",
"forecasted_time": "2017-03-24T00:20:00",
"minimum_dbz_forecast": 15,
"maximum_dbz_forecast": 25,
"average_dbz_forecast": 35,
"confidence_in_forecast": 0.99,
"alert_lat": 44.5876,
"alert_lon": -68.1039
]
}
I am hoping to A) Easily call individual profiles instead of searching for unique profile ids and B) Only have to load one JSON file to get all profile contents. An output of something like this would be more ideal:
{
"profile": [
{
"alert_id": 1,
"alert_name": "Test",
"initialized_time":"2017-03-24T00:00:00",
"alert_lat": 44.3876,
"alert_lon": -68.2039,
"profile_data": [
{
"forecasted_time": "2017-03-24T00:10:00",
"minimum_dbz_forecast": 0,
"maximum_dbz_forecast": 10,
"average_dbz_forecast": 5,
"confidence_in_forecast": 0.99
},
{
"forecasted_time": "2017-03-24T00:20:00",
"minimum_dbz_forecast": 5,
"maximum_dbz_forecast": 15,
"average_dbz_forecast": 10,
"confidence_in_forecast": 0.99
}
]
},
{
"alert_id": 2,
"alert_name": "Test2",
"initialized_time": "2017-03-24T00:00:00",
"alert_lat": 44.5876,
"alert_lon": -68.1039,
"profile_data": [
{
"forecasted_time": "2017-03-24T00:10:00",
"minimum_dbz_forecast": 10,
"maximum_dbz_forecast": 20,
"average_dbz_forecast": 15,
"confidence_in_forecast": 0.99
},
{
"forecasted_time": "2017-03-24T00:20:00",
"minimum_dbz_forecast": 15,
"maximum_dbz_forecast": 25,
"average_dbz_forecast": 35,
"confidence_in_forecast": 0.99
}
]
}
]
}
How would I go about querying my database and populating this JSON structure? Are there any Cakephp tools that help do this? Does reframing the JSON into this structure seem to make sense?
Thanks in advance!
Thanks to user ndm, I realized there were a few problems with my approach. I thought having all data in one table would simplify things, but in reality it would make things more complicated and require redundant data storage (eg, a latitude and longitude value stored for every profile entry, instead of just once in a separate table).
ndm also mentioned
You'd just have to set up the associations properly, and contain the associated >table in your find, and in case the property name for the association would be >profile_data, you wouldn't even have to modify the results at all.
After altering the Table Model file, I had this for a new "ProfileDataTable.php" file:
class ProfileDataTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('profile_data');
$this->setDisplayField('title');
$this->setPrimaryKey('alert_id');
$this->addBehavior('Timestamp');
$this->belongsTo('AlertData', [
'foreignKey' => 'alert_id'
]);
}
}
And this for a new "AlertDataTable.php" file:
class AlertDataTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('alert_data');
$this->setDisplayField('title');
$this->setPrimaryKey('alert_id');
$this->addBehavior('Timestamp');
$this->hasMany('ProfileData', [
'foreignKey' => 'alert_id'
]);
}
}
The important lines here being "belongsTo" and "hasMany".
I then was able to alter my query and use "contain" to easily link the two tables together and get the JSON formatted exactly how I wanted:
$this->AlertData->find(
'all',
['conditions' =>
['AlertData.user_id =' => $uid],
'contain' =>
['ProfileData']
]
);
For example, we have a songs table, and a favorites (songs) table.
If I use Songs::with('favorites')->get(), it will return as follows:
"songs": [
{
"id": 43,
"name": "Song 1",
"favorites": [
{
"id": 52,
"user_id": 25,
"song_id": 43
}
]
},
{
"id": 44,
"name": "Song 2",
"favorites": []
},
What I want to do is, in case the song has been favorited, return 1, and if not, return 0, as follows:
{
"id": 43,
"name": "Song 1",
"favorites": true (or 1)
},
{
"id": 44,
"name": "Song 2",
"favorites": false (or 0)
},
Is there a way to do it without having to run through the returned collection array manually in PHP?
I think this is the simplest solution using Eloquent
Song::withCount([
'favorites' => function ($query) {
$query->select(DB::raw('IF(count(*) > 0, 1, 0)'));
}
])->orderBy('favorites_count', 'desc');
There are a bunch of different ways to do what you want, it all depends on what works best for you.
If you want the result returned in your result set, you can setup a scope:
class Song extends Model
{
public function scopeAddFavorite($query, $userId = null)
{
$andUser = !empty($userId) ? ' AND favorites.user_id = '.$userId : '';
return $query->addSelect(\DB::raw('(EXISTS (SELECT * FROM favorites WHERE favorites.song_id = songs.id'.$andUser.')) as is_favorite'));
}
}
Since this scope modifies the 'select' statement, you need to make sure to manually specify the other columns you want before adding in the scope.
$songs = Song::select('*')->addFavorite()->get();
I added in the ability to pass in the user id, in case you wanted to specify that the column only return true if the song has been favorited by a specific user.
$songs = Song::select('*')->addFavorite(25)->get();
Another option, you can add an accessor to your model that will handle the check for you. You can read about accessors here.
class Song extends Model
{
// only do this if you want to include is_favorite in your json output by default
protected $appends = ['is_favorite'];
public function getIsFavoriteAttribute()
{
// if you always want to hit the database:
return $this->favorites()->count() > 0;
// if you're okay using any pre-loaded relationship
// will load the relationship if it doesn't exist
return $this->favorites->count() > 0;
}
}
Usage:
$song = Song::find(1);
// access is_favorite like a normal attribute
var_dump($song->is_favorite);
// with $appends, will show is_favorite;
// without $appends, will not show is_favorite
var_dump($song);
Using a raw SQL approach
DB::select('SELECT s.*, (CASE WHEN f.id IS NULL THEN 0 ELSE 1 END) as favorites FROM songs s LEFT JOIN favorites f ON s.id = f.song_id GROUP BY s.id')
will return the following structure:
[
StdClass { 'id' => 1, 'name' => 'Song 1', 'favorites' => 1 },
StdClass { 'id' => 2, 'name' => 'Song 2', 'favorites' => 0 },
StdClass { 'id' => 3, 'name' => 'Song 3', 'favorites' => 1 },
]
Now, using Eloquent
Song::withCount('favorites')->get()
will return the an array of objects of the Song class
[
Song { 'id' => 1, 'name' => 'Song 1', 'favorites_count' => 1 },
Song { 'id' => 2, 'name' => 'Song 2', 'favorites_count' => 0 },
Song { 'id' => 3, 'name' => 'Song 3', 'favorites_count' => 3 }
]
The difference is the first will return an array of PHP standard objects while the second an array of Song objects and the first is faster than second.
You can't do something like this without somehow manipulating the original results object first.
You don't really specify how you need to use or iterate through the data, but maybe Query Scopes can help you? Either way you'll need to iterate over the data once to manipulate it. A high order function like map will help you do this.
suppose you have the $songs array, you can do this,
foreach($songs as $song)
$song["favorites"] = is_null($song["favorites"]) ? true : false;