When multiple meta values are used for same meta key, WordPress store the values in different database rows. So every array, is stored in one row.
Initial Data
Let’s assume you have an array like this:
$family = [
'father' => [ 'John', 'Doe', 40 ],
'mother' => [ 'Jane', 'White', 39 ],
'child_1' => [ 'Luke', 'Doe', 5 ]
];
If you store each sub array as a different meta row, the only identifier for that row is the meta_id
but it’s not really meaningful and is hard to get using WordPress functions.
The “map” approach
A workaround could be to store a map of values / meta IDs in another post meta.
That can be done thanks to the fact that both update_post_meta()
and add_post_meta()
return the meta ID just added.
Store Data
So you can do something like this:
$map = [];
foreach( $family as $who => $person ) {
// update_post_meta returns the meta ID
$map[ $who ] = update_post_meta( $postId, 'family_member', $person );
}
// we store the map of family "role" to meta IDs in a separate meta
update_post_meta( $postId, 'family_members_map', $map );
Retrieve Data
When you retrieve values, e.g.
$family = get_post_meta( $postId, 'family_member' );
you get a 2 dimensions unkeyed array like this:
[
[ 'John', 'Doe', 40 ],
[ 'Jane', 'White', 39 ],
[ 'Luke', 'Doe', 5 ],
]
In short you loose the sub array keys.
But you can use the map you stored and the function get_post_meta_by_id
to recreate your original data:
$map = get_post_meta( $postId, 'family_members_map', true );
$family = array_combine( array_keys( $map ), array_map( 'get_post_meta_by_id', $map ) );
And family will be equal to the original data:
[
'father' => [ 'John Doe', 40 ],
'mother' => [ 'Jane White', 39 ],
'child_1' => [ 'Luke Doe', 5 ]
]
Update Data
Update data, it’s not very hard:
$justBorn = [ 'Anna', 'Doe', 0 ];
// current map
$map = get_post_meta( $postId, 'family_members_map', true);
// add both new sub array and new meta ID to map
$map['child_2'] = update_post_meta( $postId, 'family_member', $justBorn );
// update the map
update_post_meta( $postId, 'family_members_map', $map );
An alternative: the “all in one place” approach
The approach above works, but it’s not the easiest way.
In fact, would be easier to store the whole initial associative array (the $family
array)
is a single meta instead of in different sub-arrays.
// store data
update_post_meta( $postId, 'family_members', $family );
// retrieve data (please note the `true` as 3rd argument)
$family = get_post_meta( $postId, 'family_members', true );
// update data
$family = get_post_meta( $postId, 'family_members', true );
$family['child_2'] = [ 'Anna', 'Doe', 0 ];
update_post_meta( $postId, 'family_members', $family );
To store, retrieve and update data is quite easy and there’s no additional overhead.
Advantages of the “map” approach
In most of the cases, the “all in one place” approach is the most convenient.
But if there are hundreds or even thousands of sub arrays, using an unique meta value to store them all would result in a very large amount of memory used to fetch and parse data.
Using different rows and a modern PHP feature like generators, that amount of memory can be drastically reduced.
Moreover, when dealing with existing data (maybe inherited from old version of the website), this approach could be helpful to obtain meaningful information without conversion of old data.
Finally, having smaller and more characterized sub arrays stored in different DB rows, a query for posts using meta query and LIKE
against the serialized array in the meta value could be easier and more reliable, even if the actual reliability depends a lot on the structure of the data.