From Michael’s Attic
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 Up – Features and Contracts – Dot Notation
While working on Spider-Graph and Midas (which we’ll here more about later), I decided to extract some core classes 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:
- Be stupid simple and lean — no extra features or code
- Create, Retrieve, Update, Delete and confirm/deny single items or complex items (array)
- Allow for fallback values if get() no item
- Handle deeply nested items through dot-notation (this.one.here)
- 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. In this post, I will lead you through, step-by-step, the entire process. 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:
- I don’t use scrutinzer, so I delete that file
- 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.
- 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:
- Describe the feature
- Make it Red: Write a test for that feature. This test will fail because you haven’t actually coded yet.
- Make it Green: Write the least amount of code possible to make that test pass.
- Refactor with complete safety. The test will fail if you break something.
- 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 Michaels\DataManager(); $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 Michaels\Manager\Test; 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 Michaels\Manager\Test; use Michaels\Manager\DataManager 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 Michaels\Manager; /** * Manages Basic Items * * @package Michaels\Midas */ 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.
- Start by creating a protected property called $items and set it to an array.
- Inside add(), simply append the desired item to $this->items
- Inside get(), return $this->items[$alias];
- Inside getAll() return $this->items;
So, your DataManager class looks like:
namespace Michaels\Manager; /** * Manages Basic Items * * @package Michaels\Midas */ 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!
Filed under: Blog, Michael's Attic, Tutorials Tagged: beginner, data management, git, php, series, tdd, testing, tools, workflow
