Using Laravel to Create a March Madness Pool
One of the most time-honored traditions at Bulldog Creative is the Bulldog Battle, a March Madness pool for bragging rights and dough that's been in existence for over a quarter century and has grown to include nearly 300 entrants each year. The Battle has a unique scoring system of Seed times Round that can't be easily found on the large bracket challenge sites like Yahoo or ESPN so for most of its history, the Battle's entries were painstakingly scored by tired hands and eyes. Finally, in 2014, we decided to move the contest online by creating our own site.
Version 1.0 of the Battle site was a success. Having it online increased the number of entries, removed the need to mail in brackets, saved countless hours of scoring, and allowed participants to view up to the minute results instead of having to wait for all of that scoring to be completed.
However, while the code behind site worked, it did have some weaknesses. The front end relied on inelegant jQuery while the back end was straight up spaghetti coded PHP. It was poorly documented and would have been nearly impossible for anybody other than me to take the necessary steps to update it each year. In fact, I actually did maintain the site in 2015 and 2016 even though I wasn't working for Bulldog while living in Austin, TX at that time. It was slightly embarrassing looking back on code I had written five years ago, but hey, if you're still using the same practices half a decade later, you're probably not growing much as a developer. Anyways, our goal this year was to improve the code behind the Battle site, and we decided Laravel was the perfect choice.
This is probably a good time to talk about what exactly Laravel is. It's the most popular PHP framework out there today, and it's free, open-sourced, and extremely well documented. Laravel uses the classic Model-View-Controller (MVC) architecture. For somebody who's not a developer, let's just say that means it does a good job of keeping your code well organized. Laravel also uses Eloquent, a powerful tool known as an Object-Relational Mapper (ORM) that makes it very simple to retrieve your data and supply it to the view.
One of the best things about Laravel is that it takes care of a lot of the boring stuff for you. Setting up user authentication is something (almost) nobody enjoys doing. Our old system for forgotten passwords was basically just telling the user we would delete their account so they could create a new one with the same e-mail address. With Laravel, you get an elegant solution straight out of the box that requires minimal setup. That benefit alone was worth the effort.
How Does it Work?
I don't want to turn this into a detailed step-by-step tutorial on how to setup a Laravel project, but I do want to share some code examples to give you a taste of the framework in action.
One of the first things you'll want to do after installing Laravel is to define how the tables that store your data should be constructed through what are called migrations. For our Brackets table that holds the info for every entry in the pool, we would run php artisan make:migration create_brackets_table in the command line. This creates a scaffolded file in our Migrations folder, and here's what the important parts of that file look like after we're finished setting up the columns:
Each line in the Schema::create function is responsible for creating a new column on the Brackets table and defining what type of data can be stored in that column whether it be a string, integer, date, or larger amounts of text. Let's go over what we have for each column:
- id: Every table should have a unique id, and this is handled by the increments method.
- user_id: Every bracket belongs to a user so this our foreign key. You'll see how this relationship is defined in the model later.
- year: This simply keeps track of the year so we can separate 2019 entries from past and future entries.
- name: This allows the user to give their entry a (hopefully) funny name
- g1-g63: Here we're creating 63 columns - one for each game in the tournament - that store the user's picks for each of those games. A for loop is used so we don't have to write 63 separate lines. You'll also notice the nullable method here for the first time. That tells the table that it's okay for that column to have an empty value because we don't want to force the user to make all of their picks at once. They might want to save some of their picks then come back later and finish them.
- tiebreaker: Just a simple number for the total score of the final game.
- comments: Our boss likes to give contestants a hard time for their picks, and this is the space for his comments. MediumText is used here instead of a string to allow for more space.
- timestamps: Laravel uses this method to automatically make created_at and updated_at columns. Those can always be useful.
After you're finished making the migration, you can simply run php artisan migrate in the command line, and Laravel will take all of the tables from your migration files and create them in your database. You might also notice the dropIfExists method inside of the down function. This allows you to delete the table by running php artisan migrate:rollback.
Each of our tables needs its own corresponding model that allows us to interact with the data stored in those tables. To create the Bracket model, we simply run php artisan make:model Bracket. As with a migration, we get a scaffolded file automatically created that we can make changes to.
The main things we're going to want to add to our models are the relationships between them. If you remember our Bracket migration file, we created a column called user_id so we knew which user an entry belonged to. To define that relationship, we'll add the following to our Bracket model:
Here we're just saying that a Bracket belongs to a User. This will make it much easier to retrieve data for our views later. Conversely, in the User model, we do the inverse of that relationship. A User can fill out more than one Bracket so we use the hasMany method:
Now that we've got our data and relationships ready to go, it's time to collect that data to get it ready for the view the end user will see, and we'll do that using controllers. Continuing along with the Brackets example, we'll make a controller by running php artisan make:controller BracketsController --resource. As with the migrations and models, this automatically creates a file in the Controllers folder.
The --resource we added to the end of the command was optional, but it saves a lot of time by creating several methods inside the controller. The Create & Store methods deal with a user making their bracket, the Edit & Update methods are for updating that bracket, Destroy is obviously for deleting the bracket, and Show will give us all of the brackets. Let's look at how we do that last method:
As you can see, Laravel makes it pretty simple to retrieve all of the brackets. Of course, we don't actually want all of the brackets - just the ones in the 2019 contest, and that's what we're doing in the first line of the method. On the following line, we're sorting them by who has the highest total score, and the final line passes the data to our corresponding view where it can be accessed by a variable named brackets.
Finally, we're ready to display our magic for all the world to see. The brackets.show that we called in the controller checks for a file named show.blade.php in the resources/views/brackets folder. Blade is flexible the templating engine that comes with Laravel, and you'll see it in action below in our view file:
This is a simplified version of the standings page where we're displaying all of the 2019 brackets in a table with their name and score along with the name of the user they belong to. Blade's @foreach command is being utilized to loop through all of the brackets we passed from the controller. The relationship we set up earlier between the Bracket and User models pays off here as we simply use $bracket->user->name to output the user's name. You can think of anything in between the double brackets in the view as being equivalent to PHP's echo command.
There's of course so much more to learn about Laravel. For brevity, we didn't discuss important concepts such as configuring the database, dealing with forms, or route handling. But now that you've seen some of Laravel's core concepts, hopefully you have a good idea of why it's such a powerful and convenient framework. Check out the official documentation to get started with your own project, or give us a call if you need a website built. There's a good chance we'll use Laravel!