Category: Code

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.

Building a Data Manager Part I: Setting Up

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-1 or use the finished, feature-complete, supported DataManager at chrismichaels84/data-manager

Setting UpFeatures and ContractsDot Notation

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.

Getting Started

Step One: Create a new Repository

I like to start directly from Github. Login and create a new repository. I named my data-manager (chrismichaels84/data-manager), gave it an MIT License, and a Composer .gitignore. Though it doesn’t matter, We’re about to override all this.

Next, clone your repository locally so you can edit it more easily. I use PHP Storm 8.2, but Github’s program and Sublime Text works just as well. I’m going to assume you know how to do this. If not Github’s bootcamp is the perfect place to start.

Step Two: Create the Skeleton

I’m a big fan of being lazy. If someone else has done it, I’m gonna steal it if I can, lol. In this case, the League of Extraordinary Packages has done it right and wants us to steal it. The League is a collective of php coders who share their work. Some really good work that is held to the highest standards. They have a skeleton repo that gets all the boilerplate for a kick-ass composer package. It’s also a good idea to take a look at the PHP Package Checklist.

Clone the skeleton repo (don’t just download the zip file). Now, you can copy everything in the skeleton to your manager project. Overwrite anything that’s already there.

With a little tweaking, customize this skeleton:

  1. I don’t use scrutinzer, so I delete that file
  2. Work through the .MD files to change names, authors, and such. You can leave the badges at the top of the README.md file alone for now.
  3. Update composer.json to your awesome project.

Test Driven Development

Skip this if you’re familiar with the TDD workflow.

It is important to do things right the first time so you don’t have to do them right the second time. That’s what test driven development is all about. For the beginner (and even the advanced user), I know that phrase can sound scary, like your adding a ton of work. Plus terms like mock, stub, dummy, and acceptance are defined a million different ways — never consistently. I feel your pain.

TDD is simply writing automated tests for every feature of your package so that as you change things in the future, you can make sure you didn’t break something along the way. We all test, even if you just pull it up in the browser and put it through its paces. The problem with that is, you will miss something and a bug will creep in. These automated tests will tell you exactly what has gone wrong every step of the way. And, once you write a good test, you shouldn’t have to rewrite or rexecute it unless you make sweeping changes to the package.

You also ensure that you don’t write any extra code. You only test what you need and only code to pass the test. The basic process is:

  1. Describe the feature
  2. Make it Red: Write a test for that feature. This test will fail because you haven’t actually coded yet.
  3. Make it Green: Write the least amount of code possible to make that test pass.
  4. Refactor with complete safety. The test will fail if you break something.
  5. Repeat for each feature.

You’ll see what I mean as we plow through. Check out Laracasts or this article for great getting started lessons.

Make a Plan

I start every project by creating a PROPOSAL.md file where I figure out exactly what it is I want to do. My goals and what features I want. In this case:

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

Step Three: Work Out Your Basic API

I also like to sketch out a basic API for the class (usually in the proposal):

$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

While we’re at it, let’s extract it to an interface to src/DataManagerInterface.php and docblock everything.

Give Me Some Code, Already!

I hear you. We are finally ready to start coding. It may seem like a lot, but all this preliminary stuff is important, will make our lives so much easier, and will get faster with practice.

Step Four: Write and Fail Your First Test

Inside “/tests” create “DataManagerTest.php”

namespace MichaelsManagerTest;

class DataManagerTest extends \PHPUnit_Framework_TestCase
{
    public function testMyFirstFeature()
    {
        $this->assertTrue(true);
    }
}

Be sure to change the namespace!

Before we can run this, we need to make sure we have PHPUnit installed. It couldn’t be easier. Open up composer.json. It should already be added under “require”. If it is, then run “composer update” from the project directory in your terminal.

Once PHPUnit is installed, we can run “phpunit” from the terminal.

All is great! Except we don’t want it to be. Remember we actually want to fail our first test. It passes because we aren’t actually testing anything. Let’s change that.

namespace MichaelsManagerTest;
use MichaelsManagerDataManager as Manager;

class DataManagerTest extends PHPUnit_Framework_TestCase
{
    public function testAddSingleItem()
    {
        $manager = new Manager();
        $manager->add('alias', 'value');

        $this->assertArrayHasKey('alias', $manager->getAll(), 'Array Items does not have key `alias`');
        $this->assertEquals('value', $manager->get('alias'), 'Failed to get a single item');
    }
}

Here, we are testing a few things. First, we create a new Manager instance. Then, we try to add a string called “alias” with a value “value”. We test to see if that has worked by getting all the values from the Manager and making sure “alias” is one of them and then trying to get “alias” by itself and ensuring that the value is correct.

We are actually test 3 of our API methods: “add(), getAll(), and get()”.

Give it a whirl! Run phpunit”. What? Fatal error? Of course, we haven’t actually created the DataManager class.

Create “DataManager.php” inside “/src” with our three testable methods that do nothing right now. Don’t implement any interfaces quite yet.

namespace MichaelsManager;

/**
 * Manages Basic Items
 *
 * @package MichaelsMidas
 */
class DataManager
{
    public function add($alias, $item)
    {
        return $this;
    }

    public function get($alias)
    {
        return false;
    }

    public function getAll()
    {
        return [];
    }
}

Now when we run this test, it will not give us any errors, but it will fail. That’s actually what we want!

Step Five: Pass Your First Test

Alright, let’s make DataManager do something. Remember, we are trying to do the least amount possible to make this one test pass.

  1. Start by creating a protected property called $items and set it to an array.
  2. Inside add(), simply append the desired item to $this->items
  3. Inside get(), return $this->items[$alias];
  4. Inside getAll() return $this->items;

So, your DataManager class looks like:

namespace MichaelsManager;

/**
 * Manages Basic Items
 *
 * @package MichaelsMidas
 */
class DataManager
{
    protected $items = [];

    public function add($alias, $item)
    {
        $this->items[$alias] = $item;
        return $this;
    }

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

    public function getAll()
    {
        return $this->items;
    }
}

And viola! When we run “phpunit” our tests are green. We have successfully managed some data. Don’t forget to DocBlock your methods.

You can see the finished version for the first part at https://github.com/chrismichaels84/data-manager/tree/tutorial-part-1 or use the finished, feature-complete, supported DataManager at https://github.com/chrismichaels84/data-manager

From here on, we are just going to repeat this project until all our goals are complete and our API is functional including dot notation. Hope to see you next time!

PHP Graph Database Tools

I have talked about Graph Databases and PHP. It occurred to me that there are any number of fantastic tools available now. So this is a curated list of several graph databases with bindings in PHP, some comparison between them, and a few other tools that are database agnostic. These NoSQL databases can have some incredible benefits. It is definitely worth checking out.

Tinkerpop

The GraphDB landscape is still emerging, and a lot of technologies and people are fighting to define it. It’s especially difficult since SQL has dominated since, well, the birth of the internet. Developers know SQL and are comfortable with SQL database structures. Many of the burgeoning NoSQL (not only sql) databases try to mimic SQL in a lot of ways.

But, in many ways, graph databases are just different. They require a different way of thinking. There is no standard, no Structured Query Language that everyone agrees on. At least not yet.

Enter the Tinkerpop group, a collection of very smart engineers who are slowly standardizing graph databases. If you want to use graph data, tinkerpop is your first step, and NOT just for PHP. In fact, all of their tools are language agnostic.

Read More

PHP, Graph Databases, and the future

Graph. Databases. Are. Awesome.

At least in certain use cases.

Who knows whom, and how? Graph data.

By way of a quick intro, a graph database is a type of datastore called NoSQL or “Not Only SQL”. Since the beginning of the interwebs, the dominant form of databases were SQL, with MySQL taking the lead. And MySQL is great. However, when your data is highly related in complex ways, SQL starts to crack. Tack onto that the enormous amounts of data manipulated by today’s applications, and SQL really starts to slow things down both in the actual read/write operations and in the development of code because of ridiculously complicated JOIN statements.

This is where graph databases shine because they treat the relationships between data as first class citizens, not an after thought. Therefore, you data is actually saved as you would expect relational data to be saved: as a property graph.

Now, its not only easier to visualize, but much, much faster to traverse. Graph databases shine in asking questions about your data like:

Read More

Powered by WordPress & Theme by Anders Norén

%d bloggers like this: