Reset value on map / transform fucntion failed - Laravel - php

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.

Related

php arrange repeating items in an object

this object:
"permission_category": [{
"id": 2,
"name": "user",
"permissions": {
"id": 2,
"name": "userCreate"
}
},
{
"id": 2,
"name": "user",
"permissions": {
"id": 3,
"name": "userUpdate"
}
}
]
can i convert it to this?
"permission_category": [{
"id": 2,
"name": "user",
"permissions": [
{
"id": 2,
"name": "userCreate"
},
{
"id": 3,
"name": "userUpdate"
},
]
}
]
Do I have such a chance? Because if I return this with foreach it will print the same category name more than once. Is it possible to show both userCreate and userUpdate permissions belonging to the user category under one category? The object looks like this to me. I have created a relationship between 4 tables in Laravel. I will share their code with you too. Please tell me if what I need to do is change the code between relationships. Please tell me if what I need to do is to edit the array in the output with php array functions. I don't know anything about this, I need some help. Sorry for prolonging the topic. I'm sure there's a right and easy way to do this. Since I don't know, I had to ask. I would like to express my thanks and regards to those who have helped in advance.
Laravel Relationship:
UsersController.php
public function add() {
$data = array();
$user = AdminUsers::where('id', $this->uid())->first();
if($user->hasAdministrator()->first())
$data['permission_category'] = PermissionCategory::with('permissions')->get();
else
$data['permission_category'] = PermissionsResource::collection($user->permissions()->with('category')->get());
return $data;
die();
return view('admin.users.add', $data);
}
PermissionsResource.php
return [
"id" => $this->category[0]->id,
"name" => $this->category[0]->name,
"permissions" => [
"id" => $this->id,
"name" => $this->name
],
];
AdminUsers.php (Model)
public function permissions() {
return $this->belongsToMany(Permissions::class, 'admin_perms');
}
Permissions.php (Model)
public function category() {
return $this->belongsToMany(PermissionCategory::class, 'permissions', 'id');
}
admin_users table:
-id
-username
permission_category table:
-id
-name
permissions table:
-id
-permission_category_id
-name
admin_perms table:
-id
-admin_users_id foreign key
-permissions_id foreign key
do
$adminPermsWithCategory = AdminPerms::with('permission.category')
->where('admin_users_id', $user->id);->get();
return new AdminPermsResource($adminPermsWithCategory);
and in AdminPermsResource:
$data = [];
foreach ($this->resource as $item) {
if(!array_key_exists($item->permission->category->name, $data)){
$data[$item->permission->category->name] = [
"category_id" => $cat->id,
"category_name" => $cat->name,
];
}
$data[$item->permission->category->name]['permissions'][$item->permission->id] = new PermissionsResource($item->permission);
}
return $data;
and in PermissionsResource:
return [
"permission_id" => $this->id,
"permission_name" => $this->name,
];
maybe this help. I am writing without trying, sorry for the answer before.

Merge multiple CodeIgniter result sets into one array

Data queried from the database.
year 2021 = [
{
"month": 1,
"total": "1,482"
},
{
"month": 2,
"total": "5,422"
},
]
and
year 2020 = [
{
"month": 1,
"total": "2,482"
},
{
"month": 2,
"total": "6,422"
},
{
"month": 3,
"total": "7,422"
},
.........
{
"month": 12,
"total": "20,422"
},
]
Here I create a method called `GetData(), I create a loop from 1-12 based on the months in a year. Next I want to combine data in an array, but how do I combine the data?
Below is my controller model which calls the same model method over and over with different parameters:
public function GetData(){
$thn_now = 2021;
$thn_before = 2020;
$bulan = array();
for ($bul = 1; $bul <= 12; $bul++) {
$sql = $this->My_model->model_month($bul,$thn_now)->result(); // Here I make the current year parameter
// $sql2 = $this->My_model->model_month($bul,$thn_before)->result(); //here are the parameters of the previous year
foreach($sql as $row) {
$bulan[] = array(
'bulan' => $bul,
'total_now' => $row->hasil,
// how to display the previous year's total in an array ?
// 'total_before' => $row->hasil,
);
}
}
$this->set_response($bulan,200);
}
I want an output like this:
[
{
"month": 1,
"total_now": "1,482"
"total_before": "2,482"
},
{
"month": 2,
"total_now": "5,522"
"total_before": "6,422"
},
{
"month": 3,
"total_now": null
"total_before": "7,422"
},
.........
{
"month": 12,
"total_now": null,
"total_before": "20,422"
},
]
In 2021, the total is only until the month of 02, while in 2020 the total is until month 12.
If 2021 is only up to month 02 then the next array is total null.
I don't recommend that you make 24 trips to the database to collect individual totals; I'll urge you to create a new model method which delivers all of the monthly totals in a single trip.
I don't recommend allowing your controller to dictate the type of result set that is created from the active record in the model. The model should be consistently returning the same data in the same structure throughout your application.
It seems to me that your inner loop is only collecting one row anyhow, so instead of result() which creates an array of objects, just call row() which creates null or an object then immediately access the hasil property. Using the null coalescing operator, you can fall back to null when hasil is not a property of non-object null.
Professional programmers around the world (including ones that don't use English as their first language will recommend writing variables using English words. This allows a more consistent language for other developers to review and maintain your code. After all, you are using English in the rest of your php syntax, just keep it all in English.
For future flexibility, allow the "current year" to be passed in as a controller method argument. This will allow you to seamlessly view historic data (should the need ever arise). If there is no passed in argument, then default to the current year.
Hard coding the number of each month is acceptable because it will never change. However, as a general rule, try to avoid using hardcoded numbers/values in your script so that it is more flexible, easier to maintain, and more intuitive to read.
Suggested code (tolerating that you are passing up to 24 active record objects back to the controller):
public function GetMonthlyTotalsSincePreviousYear($year = null): void
{
$thisYear = $year ?? date('Y');
$lastYear = $thisYear - 1;
$result = [];
for ($month = 1; $month <= 12; ++$month) {
$result[] = [
'month' => $month,
'total_now' => $this->My_model->model_month($month, $thisYear)->row()->hasil ?? null,
'total_before' => $this->My_model->model_month($month, $lastYear)->row()->hasil ?? null,
];
}
$this->set_response($result, 200);
}
In my own application, I would have a model method that collects all of the data and a controller like this:
public function getMonthlyTotalsSincePreviousYear(int $year = null): void
{
$this->set_response(
$this->My_model->getMonthlyTotalsSincePreviousYear($year ?? date('Y')),
200
);
}
This, I believe, will assist you in developing the solution. I haven't included the entire array of $year_2020, but I hope you get the idea -
$year_2021 = [
[
"month" => 1,
"total_now" => "1,482"
],
[
"month" => 2,
"total_now" => "5,422"
]
];
$year_2020 = [
[
"month" => 1,
"total_before" => "2,482"
],
[
"month" => 2,
"total_before" => "6,422"
],
[
"month" => 3,
"total_before" => "7,422"
]
];
$output = [];
foreach ($year_2021 as $obj) {
$key = $obj['month'];
$output[$key] = $obj;
}
foreach ($year_2020 as $obj) {
$key = $obj['month'];
if (isset($output[$key])) {
$output[$key]['total_before'] = $obj['total_before'];
} else {
$obj['total_now'] = null;
$output[$key] = $obj;
}
}
$output = array_values($output);
error_log(json_encode($output));
Output
[{"month":1,"total_now":"1,482","total_before":"2,482"},{"month":2,"total_now":"5,422","total_before":"6,422"},{"month":3,"total_before":"7,422","total_now":null}]

Find difference between sets of objects

I have 2 objects and would like to find the difference between them. Return an array or object of the differences only. The two objects look as following.
{
"new": {
"crc_code": "00",
"serial_number": "239-03",
"reason": "Ir/b4c - no center rib",
"project_id": 9,
"wafer_id": 1,
"equipment_status_code_id": 7,
"plate_container_id": null,
"supplier_id": 1,
"container_slot_id": null,
"plate_quality_id": 1
},
"old": {
"crc_code": "00",
"serial_number": "239-03",
"reason": "Ir/b4c - no center rib",
"project_id": 9,
"wafer_id": 1,
"equipment_status_code_id": 2,
"plate_container_id": null,
"supplier_id": 1,
"container_slot_id": null,
"plate_quality_id": 2
}
}
What's the best way to go about this?
Update People asking what I have tried already?
Something along these lines.
array_udiff($new, $old, function ($obj_a, $obj_b) {
return strcmp($obj_a, $obj_b);
}
);
But not getting there..
Use array_diff_assoc:
return array_diff_assoc($obj->new, $obj->old);
// convert object to array
$arr = json_decode(json_encode($obj), true);
// get the diff
$diff = array_diff($arr['new'], $arr['old']);
// result
array:1 [
"equipment_status_code_id" => 7
]

Check if relationship of relationship if empty laravel 5.6?

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').

Complicated JSON with Cakephp 3

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']
]
);

Categories