Serialized array of relations to Laravel relations - php

I'm facing one problem and I can't find the perfect and best optimized solution.
So I'm kindly asking for your opinion.
Here's what bothering me.
I've got a serialized array from DB that looks like this:
a:6:{s:13:"property_type";s:1:"1";s:16:"property_feature";a:2:{i:0;s:1:"3";i:1;s:1:"4";}s:19:"property_offer_type";s:1:"5";s:19:"property_built_type";a:1:{i:0;s:2:"10";}s:24:"properties_office_phones";s:2:"13";s:15:"property_labels";a:1:{i:0;s:1:"8";}}
Here is the non-serialized version for more clarity:
Array(
[property_type] => 1
[property_feature] => Array
(
[0] => 3
[1] => 4
)
[property_offer_type] => 5
[property_built_type] => Array
(
[0] => 10
)
[properties_office_phones] => 13
[property_labels] => Array
(
[0] => 8
))
All of this keys values are stored in one single table.
This is how the table looks like:
ID | Name | Slug | .. etc
1 | A name here | a-name-here | .. other
What do you think will be the best and most optimized way to get the information related to those IDs using Laravel 8 relations, so it can be retrieved as relations just like "with()" is doing.
Thanks in advance!

Related

PHP Array Loop Help - WordPress ACF

I am creating a leaderboard in WordPress. The user submits their mileage along with the date and method in an ACF repeater field. I am pulling the data and need to output the following $leaderboard array:
Array ( [user1] => Array ( [walking] => 2 [overall_mileage] => 11 [swimming] => 9 ) [user2] => Array ( [running] => 54.25 [overall_mileage] => 54.25 ) )
I am calculating the mileage and inserting it into the array.
What's the best way to loop through this into a table so that it displays in a leaderboard with a breakdown as follows?
USERNAME | SWIMMING | WALKING | RUNNING | OVERALL MILES
Thanks in advance

How do I get MySQL query result as nested array using PHP?

Let's say I have this simple table where pos_level is the level of the position and pos_under is that the PID of the position on top of it.
In level 1, I have 'General Manager' and 'Supervisor'.
In level 2, under 'General Manager(PID: 1)' : 'Asst. Manager', under 'Supervisor(PID: 2)' : 'Marketing'.
In level 3, under 'Asst. manager(PID: 3)' : 'Sales' & 'Purchase', under 'Marketing(PID:2) : none.
+-----+-----------------+-----------+-----------+
| PID | pos_name | pos_level | pos_under |
+-----+-----------------+-----------+-----------+
| 1 | General Manager | 1 | 0 |
| 2 | Supervisor | 1 | 0 |
| 3 | Asst. Manager | 2 | 1 |
| 4 | Sales | 3 | 3 |
| 5 | Purchase | 3 | 3 |
| 6 | Marketing | 2 | 2 |
+-----+-----------------+-----------+-----------+
Now how do I make the query so I get a nested array as the result like this:
Array
(
[0] => Array
(
[pos_level] => 1
[pos_name] => General Manager
[pos_under] => Array
(
[0] => Array
(
[pos_level] => 2
[pos_name] => Asst. Manager
[pos_under] => Array
(
[0] => Array
(
[pos_level] => 3
[pos_name] => Sales
)
[1] => Array
(
[pos_level] => 3
[pos_name] => Purchase
)
)
)
)
)
[1] => Array
(
[pos_level] => 1
[pos_name] => Supervisor
[pos_under] => Array
(
[0] => Array
(
[pos_level] => 2
[pos_name] => Marketing
[pos_under] => Array
(
)
)
)
)
)
I have tried using multiple queries and using array_push using the results, but I have like around 100+ pos_name and I think it is messy, I have also tried using loop to keep running queries for each level and under, also tried using multiple tables for each level, but I am hoping I can use only 1 table and able to query the result as the nested array above for further use in my application.
All answers, comments, and suggestions are very welcomed. Thank you.
As mentioned above in the comments I doubt in general that the approach you are following is a good one. That is because it will not scale! You try to take all entries from your database and pack them into a single, hierarchical array. That means you create several copies of each entry in memory. What will happen with a growing number of entries? You face a constantly raising memory consumption of your script which will obviously make it fail ultimately.
Nevertheless I accepted the challenge and implemented a small demonstration of how to cleverly construct such nested structures with the help of references. Such references not only simplify the creation of nested structures, they also reduce the memory footprint by preventing that entries are copied by value again and again. This however will not solve the general scaling issue with this approach mentioned above, it can only help to reduce it.
I also took the liberty to make a small modification to your approach and introduce a new property "pos_over" to hold entries under an entry. Two reasons for that:
it usually is not a good idea to remove original available data and especially not to replace it with an altered meaning
the meaning of the term "pos_under" is to describe under what other entry an given entry is (to be) placed. But from the "upper" entries point of view the ones "below" it should not be referenced by "pos_under", right? That would contradict the original meaning of that term.
The following code does not depend on a database for demonstration purpose. Instead it reads the raw data from a CSV string and parses that. The actual building of the desired structure structure is marked further down by a comment. Also it should be mentioned that the code expects the original data to reference other entries only after they have been declared. So if processed from top to bottom each entry under another one can expect that other one to already exist.
Note, that the actual code to build that nested structure consists of only a mere 12 clever lines...
<?php
// the raw CSV data
$csvText = <<<CSV
PID|pos_name|pos_level|pos_under
1|General Manager|1|0
2|Supervisor|1|0
3|Asst. Manager|2|1
4|Sales|3|3
5|Purchase|3|3
6|Marketing|2|2
CSV;
// praparation of CSV data
foreach (explode("\n", $csvText) as $csvRow) {
$csvData[] = str_getcsv($csvRow, '|');
}
$csvTitle = array_shift($csvData);
$table = [];
foreach ($csvTitle as $titleKey=>$titleValue){
foreach ($csvData as $csvRow=>$csvColumn) {
foreach ($csvColumn as $csvKey=>$csvValue) {
$table[$csvRow][$csvTitle[$csvKey]] = $csvValue;
}
}
}
// creation of the structure
$catalog = [];
$structure = [];
foreach ($table as $key=>&$entry) {
$entry['pos_over'] = [];
if ($entry['pos_under'] == 0) {
$structure[] = &$entry;
$catalog[$entry['PID']] = &$structure[count($structure)-1];
} else {
$catalog[$entry['pos_under']]['pos_over'][] = &$entry;
$catalog[$entry['PID']] = &$catalog[$entry['pos_under']]['pos_over'][count($catalog[$entry['pos_under']]['pos_over'])-1];
}
}
// demonstration output
print_r($structure);
The output of above experiment is:
Array
(
[0] => Array
(
[PID] => 1
[pos_name] => General Manager
[pos_level] => 1
[pos_under] => 0
[pos_over] => Array
(
[0] => Array
(
[PID] => 3
[pos_name] => Asst. Manager
[pos_level] => 2
[pos_under] => 1
[pos_over] => Array
(
[0] => Array
(
[PID] => 4
[pos_name] => Sales
[pos_level] => 3
[pos_under] => 3
[pos_over] => Array
(
)
)
[1] => Array
(
[PID] => 5
[pos_name] => Purchase
[pos_level] => 3
[pos_under] => 3
[pos_over] => Array
(
)
)
)
)
)
)
[1] => Array
(
[PID] => 2
[pos_name] => Supervisor
[pos_level] => 1
[pos_under] => 0
[pos_over] => Array
(
[0] => Array
(
[PID] => 6
[pos_name] => Marketing
[pos_level] => 2
[pos_under] => 2
[pos_over] => Array
(
)
)
)
)
)

Combining results of Mysql Join

I am having a problem with Mysql join. I have two tables, a center_contacts table and a center_contacts_notes. center_contacts_notes are linked via a contact_id that is found in both tables.
Inside of center_contacts_notes there can be multiple rows per contact_id and I want to grab all of these and put them in a sub array of the results.
For example, here is what my center_contacts_notes table looks like:
contact_id | note
------------------------
1 test
2 hello
3 sup
1 moo
Here is where I am attempting to grab the data:
$this->db->select('center_contacts.id, FirstName, LastName, center_contacts_notes.note');
$this->db->from('center_contacts');
$this->db->join('center_contacts_notes', 'center_contacts_notes.contact_id = center_contacts.id');
Note that I am using Codeigniter 3.
Here is what I get from this:
Array
(
[id] => 1
[FirstName] => Bob
[LastName] => Smith
[note] => test
)
Array
(
[id] => 1
[FirstName] => Bob
[LastName] => Smith
[note] => moo
)
These are two different arrays inside of my results. This is impractical for my use as I need one array that contains both notes. Something like this:
Array
(
[id] => 1
[FirstName] => Bob
[LastName] => Smith
[note] => Array(test, moo)
)
Is this possible, and if so how would I accomplish it? Thanks.
I don't know if you can get the two dimensional array like you've mentioned, but there is a workaround to get similar results. This might be helpful to you.
What you need to do is use Group By and group_concat(). Group by the table by contact_id and apply group_concat() on center_contacts_notes.note.
Your query should look like this.
$this->db->select('center_contacts.id, FirstName, LastName, GROUP_CONCAT(center_contacts_notes.note)');
$this->db->from('center_contacts');
$this->db->join('center_contacts_notes', 'center_contacts_notes.contact_id = center_contacts.id');
$this->db->group_by('center_contacts.id');
By default group_concat will concat the column by ,. You can change it the following way.
GROUP_CONCAT(center_contacts_notes.note SEPARATOR 'YOUR_SEPARATOR_STRING')
This will return result as follow:
Array
(
[id] => 1
[FirstName] => Bob
[LastName] => Smith
[note] => test[YOUR_SEPARATOR_STRING] moo
)
You can use PHP explode to convert note string into array by providing separator value.

Optimizing multiple queries in MySQL and PHP

Questions
How should I do the query(ies) to get this results?
Should I use a different structure for database tables?
Details
I want to get results from 3 tables:
+------------------------------+-------------------+
| courses | id | <-------+
| | name | |
| | | |
+------------------------------+-------------------+ |
| sections | id | <-------|----------+
| | course_id | <- FK(courses.id) |
| | name | |
+------------------------------+-------------------| |
| resources | id | |
| | section_id | <- FK(sections.id)-+
| | name |
+------------------------------+-------------------+
I want to store results in a PHP Array like this:
Array
(
[courses] => Array
(
[id] => 1
[name] => course 1
[sections] => Array
(
[0] => Array
(
[id] => 1
[course_id] => 1
[name] => course 1 section 1
[resources] => Array
(
[0] => Array
(
[id] => 1
[section_id] => 1
[name] => resource 1
)
)
)
)
)
)
EDIT
What I did:
$cources = DB::query(Database::SELECT,
'select * from courses')->execute($db,false)[0]; // Get all courses as array
foreach($courses as &$course) {
$sections = DB::query(Database::SELECT,
'select * from sections where course_id = '.$courses['id']);
$course['sections'] = $sections;
foreach($course['sections'] as &&section) {
$resources = DB::query(...); // Get array of resources
$section['resources'] = $resources;
}
}
The database structure is normalized - this is correct and should not be changed.
However, SQL returns de-normalized or "flattened" data for an N+ join: only a set of homogenous records can be returned in a single result-set. (Some databases, like SQL Server, allow returning structure by supporting XML generation.)
To get the desired array structure in PHP will require:
Separate queries/result-sets (as shown in the post): ick!
There will about one query/object. While the theoretical bounds might be similar, the practical implementation will be much less efficient and the overhead will be much more than for single query. Remember that every query incurs (at the very least) a round-trip penalty - as such, this is not scalable although it will likely work just fine for smaller sets of data or for "time insensitive" operations.
Re-normalize the resulting structure:
This is very trivial to do with support of a "Group By" operation, as found in C#/LINQ. I am not sure how this would be approached [easily] in PHP1. This isn't perfect either, but assuming that hashing is used for the grouping, this should be able to scale fairly well - it will definitely be better than #1.
Instead of the above, consider writing the query in such a way that the "flat" result can be used within the current problem/scope, if possible. That is, analyze how the array is to be used - then write the queries around that problem. This is often a better approach that can scale very well.
1 Related to re-normalizing the data, YMMV:
SQL result to PHP multidimensional array
PHP array to multidimensional array
Group a multidimensional array by a particular value?
You can try something like this
SELECT * FROM (
select c.id, c.name from courses c
union
select s.id, r.name,s.course_ID from sections s
union
select r.id, r.name,r.section_ID from resources r
)
You cant get multi dimensional result from mysql. The query for getting the elements should be like this:
select courses.id as coursesId,courses.name as coursesName,sections.id as sectionsId,sections.name as sectionsName,resources.id as resourcesId, resources.name as resourcesName
from courses
left join sections on courses.id=sections.course_id
left join resources on sections.id=resources.section_id;
But ofcourse it will not give you the array as you like.
if you are familiar with php then you can use this code i am writing only 2nd level you can write same way with third label
$final=array();
$c=-1;
$cid=false;
$cname=false;
$query = "SELECT c.*,s.*,r.* FROM courses AS c LEFT JOIN sections AS s ON c.id=s.course_id LEFT JOIN resources AS r ON r.section_id =s.id";
$result=mysql_query($query, $this->con) or die(mysql_error());
while($row= mysql_fetch_array($result)){
if($cid!=$row[2]){
$final['cources'][++$c]['id']=$cid=$row[0];
$final['cources'][$c]['name']=$cname=$row[1];
$s=-1;
}
$final['cources'][$c]['sections'][++$s]['id']=$row[2];
$final['cources'][$c]['sections'][$s]['course_id']=$row[3];
$final['cources'][$c]['sections'][$s]['name']=$row[4];
}
echo "<pre>";
print_r($final);
echo "</pre>";
//Outpur
Array
(
[cources] => Array
(
[0] => Array
(
[id] => 1
[name] => c1
[sections] => Array
(
[0] => Array
(
[id] => 1
[course_id] => 1
[name] => s1-1
)
[1] => Array
(
[id] => 1
[course_id] => 1
[name] => s1-1
)
)
)
[1] => Array
(
[id] => 2
[name] => c2
[sections] => Array
(
[0] => Array
(
[id] => 2
[course_id] => 2
[name] => s1-2
)
)
)
)
)

Insert Array values, multiple rows

I'm trying to record some information on a database. My post looks like this:
Array
(
[Action] => 1000
[Date_Stat] => 07/02/2013
[Date_Final] => 07/02/2013
[Product_Id] => Array
(
[0] => 2
[1] => 6
[2] => 1
)
[Conversion] => Array
(
[0] => 1,20
[1] => 1,00
[2] => 2,03
)
[ValueMin] => Array
(
[0] => 2,00
[1] => 1,58
[2] => 2,70
)
[ValueMax] => Array
(
[0] => 2,50
[1] => 1,98
[2] => 2,90
)
[ValueMedio] => Array
(
[0] => 2,20
[1] => 1,68
[2] => 2,80
)
)
HOW can I insert all this on database the right way?
I'm not sure about the best way to design the tables and store the information. I'm using this to make a PRICE TABLE with starting date, final date and list all products with prices.
Also I'm thinking what is the best method. There are 2 possibilities I think about
Date_Start | Date_End |Product_Id | ValueMin | ValueMax | ValueMedio | Conversion
02-02-2013 02-03-2013 1 1.00 2.00 3.00 4.00
02-02-2013 02-03-2013 2 1.00 2.00 3.00 4.20
02-02-2013 02-03-2013 3 1.00 2.00 2.00 4.40
OR (using implode and putting all values on the same row)
Date_Start | Date_End |Product_Id | ValueMin | ValueMax | ValueMedio | Conversion
02-02-2013 02-03-2013 1,2,3 1,1,1 2, 2,2 3,3,2 4, 4.3, 4.4
Thanks a lot!
Choose the option mentioned first. Selecting Rows will become much easier if you do it that way.
To insert the records, use a simple prepared Statement (PHP Manual) and use a for-Loop.
I'd suggest a little tweak over the first option. If you create a separate table (Something along the lines of "Prices") like this:
id|DateStart|DateEnd|
1|02-02-2013|02-02-2013|
And then, in the table you suggested, you'd replace the date range for the PriceId (Foreign Key to this table). That way it'll be easier for you to maintain the consistency over changes on date ranges.

Categories