Map array structure in php

883

I need to find a nice solution to map one database model to another, third party model.

The code currently has to do something like:

'HomePhone' => $customer['details']['phone']['home']

for example.

As you can imagine, if$customer['details']['phone']['home'] is used multiple times, it becomes a nightmare to modify the keys if the database changes (as it does).

So I would like to have a config to use for the mapping, for example

$map = [
    'HomePhone' => 'customer.details.phone.home'
]

Such that, I can simple use$map['HomePhone'] throughout, and only need to change the one spot for the data model, eg to change it tocustomer.details.homephone

If this is the structure of the config item, how would I takecustomer.details.phone.home and explode it to[customer][details][phone][home] within the assignment above ?

Is this thinking valid, or is there a better way ? Thanks

906

Answer

Solution:

welcome to data abstraction

Generally speaking, if you have some datum and you want to get a specifc piece of data out, you write a selector. The good news is, you're on the right path – only you're just trying to solve it a little incorrectly …

// some data
$customer = [
  // ...
  'details' => [
    // ...
    'phone' => [
      // ...
      'home' => '0123456789'
    ]
  ]
];

// a selector
function customerHomePhone($customer) {
  return $customer['details']['phone']['home'];
}

Now whenever you need to access this property in your code, you will always do so using

<span>Home Phone: <?php echo customerHomePhone($customer) ?></span>

The benefits of this are absolutely tremendous.

The person trying to get data from$customer doesn't need to know anything about how it's structured.

What if the data were to become extremely complex, with hundreds of fields and dozens of levels of nesting? It might be really frustrating to have to look at a reference object to re-learn how to access a field you need to read.

I can always remembercustomerHomePhone($customer) but it's much harder to remember …

$customer['details']['phone']['home']

"Was itdetail singular ordetails plural? Wait, yeah, I thinkphones was pluralized butdetail was singular... dammit !"


What it also means is that you have freedom to change where the home phone data (or any data) is stored – change it however you want – and all you have to do is update it one place in your app.

Say you change it to be stored as …

$customer['contacts']['home_phone']

… all you have to do is updatecustomerHomePhone to …

function customerHomePhone($customer) {
  return $customer['contacts']['home_phone'];
}

Or$customer might even change more significantly. Maybe you'll usestdClass instead of array so the selector becomes …

function customerHomePhone($customer) {
  return $customer->details->phone->home;
}

It doesn't matter. As long as the selector points to the correct value in whatever data structure you're using, the rest of your app is shielded from the change and continues to work perfectly.


Of course it would make sense to build other selectors as well …

function customerDetails($customer)
  return $customer['details'];
}

function customerPhones($customer) {
  return customerDetails($customer)['phone'];
}

function customerHomePhone($customer) {
  return customerPhones($customer)['home'];
}

Would you like to learn more about data abstraction? Check out these links:


💩 OOP

Maybe you don't like the functional approach, or maybe OOP gels better with your existing app.classes are OOP's golden hammer when it comes to data abstraction – or any abstraction for that matter.

class Customer {
  private $data;
  public function __construct(array $data) {
    $this->data = $data;
  }
  // maybe we only use details internally, thus private
  private function details() {
    return $this->data['details'];
  }
  public function first_name() {
    return $this->details()['first_name'];
  }
  public function last_name() {
    return $this->details()['last_name'];
  }
  public function full_name() {
    return $this->first_name() . ' ' . $this->last_name();
  }
}

$customer = ['details' => ['first_name' => 'N', 'last_name' => 'K']];
$c = new Customer($customer);
echo $c->full_name(); //=> 'N K'

This might seem like a lot of work, but that's only because your$customer data is so complicated. If your customer data was flat, you'd still want to use data abstraction, but you could do so in a much more simple way …

class Customer {
  private $data;
  public function __construct(array $data) {
    $this->data = $data;
  }
  public function __get($key) {
    return array_key_exists($key, $this->data) ? $this->data[$key] : null;
  }
  public function full_name() {
    return $this->first_name . ' ' . $this->last_name;
  }
}

// flat data
$customer = ['first_name' => 'N', 'last_name' => 'K'];
$c = new Customer($customer);
echo $c->full_name(); //=> 'N K'

People are also looking for solutions to the problem: WordPress Site hacked? Suspicious PHP file

Source

Didn't find the answer?

Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.

Ask a Question

Write quick answer

Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.

Similar questions

Find the answer in similar questions on our website.