Code.

Yeah, I’m a programming nerd. All these new forms of storytelling and curriculum design require new tools. This part of my blog is my journal as I work with others to develop these cutting-edge apps.

Start Here: PHP, Graphs, Github, Proposals.

See My Portfolio and Resume

Latest Code Posts

See all Code posts.

Graph Database Crash Course

I am one of the lead architects for Spider, a php-based graph database abstraction library similar to Eloquent or Doctrine. In getting ready for the first truly functional release, I thought I would share some great links to a crash course in Graph Databases as a whole. You can also check out by graphs tag for other posts about graph databases and php.

Some great resources:

The real purpose of this post was to repost this incredible breakdown of the top contenders for graph databases in 2015

 

PHP Design Patterns

PHP has a reputation as a beginners or hackers language. It may have been deserved at one point, but PHP is growing both in prowess and ability. More importantly, the community is taking code quality seriously, implementing solid design patterns and good software architecture methods.

Here are a couple of my favorite design pattern articles specifically for PHP

Series: Building a Data Manager

I decided to extract some core classes I’ve used in several of my projects into their own package, a data-manager. DataManager is a container that does exactly what it says: manages item data for things like configuration settings. It also handles dot notation and exceptions. It should:

  1. Be stupid simple and lean — no extra features or code
  2. Create, Retrieve, Update, Delete and confirm/deny single items or complex items (array)
  3. Allow for fallback values if get() no item
  4. Handle deeply nested items through dot-notation (this.one.here)
  5. Be super extendable super easily

I worked for a few hours and cranked out exactly what I needed using Test Driven Development. You can use the Manager freely from github or composer. But, I wanted to share my process. This series will lead you through, step-by-step, the entire creation workflow for a php composer package using Test Driven Development. This is great for beginners who want to see TDD in practice.

You can see the finished version of this tutorial at chrismichaels84/data-manager/tree/tutorial-part-3 or use the finished, feature-complete, supported DataManager at chrismichaels84/data-manager.

Series Contents

  1. Setting Up – Define our goals and get our skeleton in place
  2. Features and Contracts – Define our API and get some core functionality
  3. Dot Notation – Dig deep into array structures (one.two.three)

Building a Data Manager Part III: Dot Notation

This post is part of a series aimed at beginning PHP coders. Follow step-by-step from ground zero to build a simple data manager.

You can see the finished version for the first part at chrismichaels84/data-manager/tree/tutorial-part-3 or use the finished, feature-complete, supported DataManager at chrismichaels84/data-manager

Setting UpFeatures and ContractsDot Notation


In the previous posts, we build a data manager from scratch using Test Driven Development. There are two features we have left to implement: deeply nested values via dot notation and throwing exceptions as needed. You can review the source code and lessons for the earlier bits, but this tutorial should stand on its own fairly well.

Dot notation is a common convention for getting at values that are deeply rooted in multidimensional arrays. The best way to show this is to show it.

$array = [
    'michael' => [
        'family' => [
            'sisters' => [
                'oldest' => 'Alicia',
                'youngest' => 'Erika'
            ]
        ],
        'favorites' => [
            'color' => 'purple',
            'foods' => ['tacos', 'pasta', 'all the bad for you stuff']
        ]
    ],
    'other' => 'value'
];

// array.michael.family.sisters.oldest returns Alicia
// array.michael.family returns sisters array
// array.michael.favorites.foods returns my favorite foods
// array.other returns value

With dot notation, we can easily dig deep into our Manager object. We want to be able to Create, Retrieve, Update, and Delete using dot notation. So, let’s get cracking.

Step Thirteen: Creating Our Tests

In this case, I want to create all of our tests at once so we know exactly what we’re getting into. I am sure we will create some private helper methods that we will reuse and it’s always good to have an overview of what will happen.

According to our API checklist, this is what’s left:

$manager->add('namespace.item', $item);
$manager->exists('namespace.name'); // also has()
$manager->get('namespace.item', $fallback);
$manager->remove('namespace.name');

And we’ll do it in that order. As we learned in the last post, set() is the same as add() and has() is the same as exists(). Looks like four tests to me.

We also have to be careful of false positives. If we add an item “one.two.three” it will create an item with that name without actually nesting arrays, so most of the tests will pass. To get by this, we will verify the entire contents of the manager in every test.

public function testAddNestedItems()
{
    $manager = new Manager();
    $manager->add('one.two.three', 'three-value');
    $manager->add('one.two.four.five', 'five-value');
    $manager->add('one.six', ['seven' => 'seven-value']);
    $manager->add('one.six.eight', 'eight-value');
    $manager->add('top', 'top-value');

    $expected = [
        'one' => [
            'two' => [
                'three' => 'three-value',
                'four' => [
                    'five' => 'five-value'
                ],
            ],
            'six' => [
                'seven' => 'seven-value',
                'eight' => 'eight-value'
            ]
        ],
        'top' => 'top-value',
    ];
    $actual = $manager->getAll();

    $this->assertEquals($expected, $actual, 'failed to add nested items');
}

public function testCheckExistenceOfNestedItems()
{
    $manager = new Manager();
    $manager->add('one.two.three', 'three-value');

// Always match against full contents
    $expected = ['one' => ['two' => ['three' => 'three-value']]];
    $actual = $manager->getAll();
    $this->assertEquals($expected, $actual, 'failed to add nested items');

    $this->assertTrue($manager->exists('one.two.three'), 'failed to confirm existence of a nested item');
    $this->assertFalse($manager->exists('one.two.no'), 'failed to deny existence of a nested item');
}

public function testGetNestedItems()
{
    $manager = new Manager();
    $manager->add('one.two.three', 'three-value');

// Always match against full contents
    $expected = ['one' => ['two' => ['three' => 'three-value']]];
    $actual = $manager->getAll();
    $this->assertEquals($expected, $actual, 'failed to add nested items');

    $this->assertEquals('three-value', $manager->get('one.two.three'), 'failed to get a single item');
}

public function testRemoveNestedItems()
{
    $manager = new Manager();
    $manager->add('one.two.three', 'three-value');
    $manager->add('one.two.four', 'four-value');

    // Always match against full contents
    $expected = ['one' => ['two' => ['three' => 'three-value', 'four' => 'four-value']]];
    $actual = $manager->getAll();
    $this->assertEquals($expected, $actual, 'failed to add nested items');

    $manager->remove('one.two.three');
    $manager->remove('does.not.exist');

    $this->assertTrue($manager->exists('one.two.four'), 'failed to leave nested item in tact');
    $this->assertFalse($manager->exists('one.two.three'), 'failed to remove nested item');
}

Great! All our tests fail or error. (Wow, that sounds weird).

Let’s Talk About Nested Arrays

Before we dive into modifying nested arrays, lets look at how we use loops to navigate nested arrays. When given a string like “this.is.my.dot.notation” what we are really wanting to do is loop through a given array 5 times, changing our location in the array by one step. So, on the first loop we go to $array[‘this’] and then to $array[‘this’][‘is’] and so on.

The best way to achieve this is to use php references from variables. What this does is allow two variables to point to the exact same bit of data. You modify one, you modify the other. For instance,

$loc = &$this->items['this']['is']['my'];

does not set $loc to the value of that array ([‘dot’ => [‘notation’]]). Instead, $loc now refers to or points to or aliases that array.

Knowing that, we can navigate through our deeply nested items using a foreach in this basic way:

$alias = "one.two.three";
$loc = &$this->items;
foreach (explode('.', $alias) as $step) {
    if (isset($loc[$step])) {
        $loc = &$loc[$step];
    }
}

We are going to modify this basic control structure (and use a while loop variant) to do all of our nesting. Note that even if an alias does not have a dot (and therefore refers to the top level), the loop will still execute once. That means we don’t need to check if an alias is nested. We can just treat all aliases as nested and the method will return the correct value.

Step Fourteen: Adding Nested Items

Let’s get our feet wet. Our add() method now is

public function add($alias, $item = null)
{
    // Are we adding multiple items?
    if (is_array($alias)) {
        foreach ($alias as $key => $value) {
            $this->add($key, $value);
        }
        return $this;
    }

    // No, we are adding a single item
    $this->items[$alias] = $item;

    return $this;
}

We want to leave adding multiple items the same. If it ain’t broke, don’t fix it. We’ll use our foreach loop to replace $this->items[$alias] = $item

// No, we are adding a single item
$loc = &$this->items;
foreach (explode('.', $alias) as $step) {
    $loc = &$loc[$step];
}
$loc = $item; // remember that $loc now refers to the right place in the nested items array

First, we set a temporary location variable to refer to our items array. Second, we loop through that items array “x” times, depending on how deep the alias is nested. At each loop, we change the $loc variable to refer to the new level. Finally, we simply set final location (which refers to the items array) to the correct value. Viola!

For now, comment out the other three tests, run phpunit, and watch everything pass.

Step Fifteen: Check the Existence of Nested Items

Since we are going to want to check the existence of things before we get or remove them (in order to avoid “index not found” errors), let’s whip this one out first.

Our current exists() method is pretty simple:

public function exists($alias)
{
    return (isset($this->items[$alias]));
}

We want to cycle through the array in the same way we did for add(). It’s good practice to return early, so we can return false if at any time we run into a nonexistent item. We return true at the end.

public function exists($alias)
{
    $loc = &$this->items;
    foreach (explode('.', $alias) as $step) {
        if (!isset($loc[$step])) {
            return false; // returning early so the loop ends
        } else {
            $loc = &$loc[$step];
        }
    }
    return true;
}

This passes our second test, and breaks no other tests. Well done.

Step Sixteen: Getting a Nested Item

Right now we’re looking at

public function get($alias, $fallback = null)
{
    $exists = $this->exists($alias);
    if (!$exists && !is_null($fallback)) {
        return $fallback;
    } elseif (!$exists) {
        throw new ItemNotFoundException();
    }

    return $this->items[$alias];
}

Let’s break this down logically. We have three ways any attempted get() can go

  1. The item does exist. Return the value
  2. The item does not exist, but we have a fallback. Return the fallback
  3. The item does not exist, and there is no fallback. Throw an error.

This is the perfect time for an if, elseif, else statement. When it comes time to return the value if it exists, we will use the same foreach structure as we did to add.

// Check for existence
$exists = $this->exists($alias);

// The item does exist, return the value
if ($exists) {
    $loc = &$this->items;
    foreach (explode('.', $alias) as $step) {
        $loc = &$loc[$step];
    }
    return $loc;

// The item does not exist, but we have a fallback
} elseif ($fallback !== null) {
    return $fallback;

// The item does not exist, and there is no fallback
} else {
    throw new ItemNotFoundException();
}

 Step Seventeen: Remove a Nested Value

That brings us to the last of our CRUD for nested values. I’ll be honest. This one stumped me for a moment. I tried a simple foreach, found that foreach was going through ALL the items in the . I googled it and found Laravel’s Array Helpers which led me to this solution:

public function remove($alias)
{
    $loc = &$this->items;
    $parts = explode('.', $alias);

    while (count($parts) > 1) {
        $step = array_shift($parts);
        if (isset($loc[$step]) && is_array($loc[$step])) {
            $loc =& $loc[$step];
        }
    }

    unset($loc[array_shift($parts)]);
}

Here, we iterate over the alias array, but don’t hit the very last member.

Now all of our tests pass. We can refactor with joy because anything we change that will break something causes phpunit to throw a fit.

A Little Polish

There are just two more things I would like to do. First, add a constructor so you can populate the manager at instantiation.

First a test.

public function testPopulateAtInstantiation()
{
    $expected = ['one' => ['two' => ['three' => 'three-value']]];
    $manager = new Manager($expected);

    $this->assertEquals($expected, $manager->getAll(), 'failed to populate array at construction');
}

And to make it pass

public function __construct(array $items = [])
{
    $this->items = $items;
}

All done!

And that, my friends, is that. We have a fully featured Manager, well tested, and ready for the world. Add in some docblocks and refactor to you hearts content. See the full version at http://github.com/chrismichaels84/data-manager. There are different branches. The “master” is the up-to-date version with new features. Tutorial branches are married to these tutorials.

Cheers!

Building a Data Manager Part II: Features and Contracts

This post is part of a series aimed at beginning PHP coders. Follow step-by-step from ground zero to build a simple data manager.

You can see the finished version for the first part at chrismichaels84/data-manager/tree/tutorial-part-2 or use the finished, feature-complete, supported DataManager at chrismichaels84/data-manager

Setting Up – Features and Contracts – Dot Notation


Last time, we started building a simple data manager for things like configuration settings (or any kind of collection, really). Our feature goals were:

  • Create, Retrieve, Update, and Delete single items or complex items (array)
  • Get all items as an array
  • Clear all items
  • Confirm or deny that an item exists in its collection
  • Handle deeply nested items through dot-notation (this.one.here)
  • Throw exceptions as needed, or fail silently based on configuration
  • Allow for fallback values if get() no item

We set up a repository, defined our API, created an interface, and implemented the first feature (add and get a single item) using Test Driven Development. If you don’t remember, check it out!

Now we are going to implement the rest of the basic features and begin checking things off of our api list. We can actual check some things off now!

//$manager = new MichaelsDataManager();
//$manager->add('name', $item);
$manager->add(['name' => $item, 'name2' => $item2]);
$manager->add('namespace.item', $item);
//$manager->get('name');
$manager->get('namespace.item');
$manager->get('doesntexist', 'fallback');
//$manager->getAll();
$manager->set('name' $newValue);
$manager->set(['name1' => $newValue1, 'name2' => $newValue2]);
$manager->clear();
$manager->remove('name');
$manager->remove('namespace.name');
$manager->has('item'); // true or false
$manager->exists('item'); // same as above

Basic C.R.U.D.

Step Six: Add Multiple Items at Once

Let’s tack line 3 of the above code. It should be simple enough to add an array of items. While we’re at it, lets start adding different kinds of data, just to be on the safe side.

First, the test

public function testAddMultipleItemsAtOnce()
{
    $manager = new Manager();
    $manager->add([
        'objectTest' => new StdClass(),
        'closureTest' => function () {
            return true;
        },
        'stringTest' => 'value'
    ]);

    $items = $manager->getAll();

    $this->assertArrayHasKey('objectTest', $items, 'Array Items does not have key `objectTest`');
    $this->assertArrayHasKey('closureTest', $items, 'Array Items does not have key `closureTest`');
    $this->assertArrayHasKey('stringTest', $items, 'Array Items does not have key `stringTest`');
}

When we run “phpunit”, we get an errors. Let’s make the tests pass by fixing DataManager.php

public function add($alias, $item = null)
{
    // Are we adding multiple items?
    if (is_array($alias)) {
        foreach ($alias as $key => $value) {
            $this->add($key, $value);
        }
        return $this;
    }

    // No, we are adding a single item
    $this->items[$alias] = $item;

    return $this;
}

All this does is check if we are adding at array of items, and sends it back through the same method for each item to add.

Run “phpunit” and we are golden.

Step Seven: Confirm or deny that an item exists

Let’s do this one in two tests: First check to see if it confirms the existence of an item:

public function testReturnTrueIfItemExists()
{
    $manager = new Manager();
    $manager->add('test', 'value');

    $this->assertTrue($manager->exists('test'));
}

And then confirm that an item doesn’t exist:

public function testReturnFalseIfItemDoesNotExist()
{
    $manager = new Manager();

    $this->assertFalse($manager->exists('test'));
}

Create this method in DataManagerTest.php and run the unit test. It should fail. Yep. To make it pass, were just going to return the result of isset()

public function exists($alias)
{
    return (isset($this->items[$alias]));
}

All green. Good job. See how easy this actually is? Since we also have a has() method, lets just point it to exists().

public function has($alias)
{
    return $this->exists($alias);
}

Step Eight: Allow for fallback values

If we try to get an item that doesn’t exist, right now it would throw an error. I want to be able to provide a fallback value if my item doesn’t exist, so I know I’ll get a default value.


$manager->get('doesntexist', 'fallback');

Like always, we write the test first so that it fails or errors:

public function testCanProvideFallbackValue()
{
    $manager = new Manager();
    $manager->add('one', 'one-value');

    $actual = $manager->get('two', 'default-value');

    $this->assertEquals('default-value', $actual, 'failed to return a fallback value');
}

We get an undefined index error. That’s what we expect. There is no index for ‘two’. Let’s make it pass using the exists() method we’ve already made.

public function get($alias, $fallback)
{
    if (!$this->exists($alias) && $fallback) {
        return $fallback;
    }

    return $this->items[$alias];
}

Well that was interesting. We made that test pass, but we broke another test. in testGetSingleItem() we use $manager->get() and don’t provide a fallback. But $fallback is required. This is why we write automated tests. It may have taken me months to come across that bug.

Let’s fix it so all our tests pass. We want fallback to be optional, so we’ll set it to null by default.

public function get($alias, $fallback = null)
{
    if (!$this->exists($alias) && !is_null($fallback)) {
        return $fallback;
    }

    return $this->items[$alias];
}

Bam! Green! Let’s move on.

If we do not have a fallback value, we should throw an exception.

Test:

/**
 * @expectedException MichaelsManagerItemNotFoundException
 */
public function testThrowsExceptionIfItemNotFound()
{
    $manager = new Manager();
    $manager->get('doesntexist');
}

Updated get() method:

public function get($alias, $fallback = null)
{
    $exists = $this->exists($alias);
    if (!$exists && !is_null($fallback)) {
        return $fallback;
    } elseif (!$exists) {
        throw new ItemNotFoundException();
    }

    return $this->items[$alias];
}

And of course, we have to create ItemNotFoundException.php inside of src.

namespace MichaelsManager;

class ItemNotFoundException extends Exception
{

}

Step Nine: Updating a Value

We have the C and R from CRUD. Let’s take on the U – Update. What we want is


$manager->set('name' $newValue);

$manager->set(['name1' => $newValue1, 'name2' => $newValue2]);

So, let’s write our tests.

public function testUpdateSingleItem()
{
    $manager = new Manager();
    $manager->add('item', 'original-value');
    $manager->set('item', 'new-value');

    $this->assertEquals('new-value', $manager->get('item'), 'failed to update a single item');
}

public function testUpdateMultipleItems()
{
    $manager = new Manager();
    $manager->add('item', 'original-value');
    $manager->add('item2', 'other-original-value');
    $manager->set(['item' => 'new-value', 'item2' => 'other-new-value']);

    $this->assertEquals('new-value', $manager->get('item'), 'failed to update first item');
    $this->assertEquals('other-new-value', $manager->get('item2'), 'failed to update second item');
}

From previous experience, we know that set() requires 2 arguments, but in the second test we only provide one. So, we will get an error. We will fix this the same way as before.

Actually, this is all identical to what we’ve done before, right? We’re just going to create or update an index in an array. Why don’t we cheat and just point to the add() method? It can’t hurt to try. We don’t want to copy and past the code because it may change in the future and we don’t want to change it twice.

public function set($alias, $item = null)
{
    return $this->add($alias, $item);
}

Everything is GREEN. Kudos to us for saving ourselves some work.

Step Ten: Remove An Item

We don’t want to use the word delete, because it can be reserved and cause weird bugs. So we want to remove individual items and we want to clear the entire manager. Let’s remove a single item first. We know our test will fail before we create the method, so I’m going to do both at the same time for brevity.

Test:

public function testRemoveItem()
{
    $manager = new Manager();
    $manager->add([
        'one' => 'one',
        'two' => 'two'
    ]);

    $manager->remove('one');

    $items = $manager->getAll();

    $this->assertArrayNotHasKey('one', $items, 'failed to remove `one`');
    $this->assertArrayHasKey('two', $items, 'failed to leave `two` intact');
}

Method in DataManager. When doing this, we want to

  1. Check if the item exists at all
  2. Remove it if it does
public function remove($alias)
{
    if ($this->exists($alias)) {
        unset($this->items[$alias]);
    }
}

Let’s look at this code for a second. We use the already created exists() method. If it does exist, then we pop the item out of the array and delete it. When it comes time to return, I used a short syntax for the if statement that says “if $removed exists) then return it, otherwise, return false”. Remove won’t exist unless we removed something.

Step Eleven: Clear the Entire Manager

As a last helper, it would be nice to totally empty the manager. This should be easy.

Test:

public function testClear()
{
    $manager = new Manager();
    $manager->add([
        'one' => 'one',
        'two' => 'two'
    ]);

    $manager->clear();

    $items = $manager->getAll();

    $this->assertEmpty($items, "Failed to empty manager");
}

Code:

public function clear()
{
    $this->items = [];
    return $this;
}

Checking Our Progress

What can we safely check off now?

//$manager = new MichaelsDataManager();
//$manager->add('name', $item);
//$manager->add(['name' => $item, 'name2' => $item2]);
$manager->add('namespace.item', $item);
//$manager->get('name');
$manager->get('namespace.item');
//$manager->get('doesntexist', 'fallback');
//$manager->getAll();
//$manager->set('name' $newValue);
//$manager->set(['name1' => $newValue1, 'name2' => $newValue2]);
// $manager->clear();
//$manager->remove('name');
$manager->remove('namespace.name');
//$manager->has('item'); // true or false
//$manager->exists('item'); // same as above

Wow, that’s everything except the namespaced dot-notation which we will tackle next time.

Step Twelve: Abiding By a Contract

So, for good measure, let’s implement an interface. This is important because it allows others to create interchangeable classes. I will discuss this more later, but it’ a really good habit. We’ll have to modify what we created earlier. If your editor won’t extract an interface, the easiest thing to do is copy and past the class into the new file and then remove all the code. Leave your docblocks!

And that is all for now. All that is left is to implement the namespacing feature and handle missing items, and then publish out work to the world! See you later.

Powered by WordPress & Theme by Anders Norén

%d bloggers like this: