Introducing SitePoint Premium Collections (and Our Best Price Ever)

Huge discounts on SitePoint Premium

We’ve just launched something new, and we’re celebrating with a huge discount on SitePoint Premium memberships.

Introducing Collections: we’ve made it even easier to watch playlists of screencasts on your favorite topics (and by your favorite teachers) in grouped bundles so you can learn more intuitively – the next instalment is right there in the relevant Collection, exactly where you need it. New videos are added daily.

Watch one lesson or binge away, the choice is yours, free for all SitePoint Premium Members.

To celebrate the launch, we’re doing something we never, ever do and offering SitePoint Premium Membership at a massive discount for one week only:

Monthly – $7 (normally $15)
Annual – $48 (normally $108)

And, for the first time ever, you can now have a SitePoint Premium Lifetime Membership! A one time cost of $199 for ongoing access to the best resources to develop your skills – forever. That’s unlimited access to all SitePoint books, courses, collections and screencasts.

We’re reverting to our usual pricing at the end of the week, so now’s the time to join!

Join SitePoint Premium

Continue reading %Introducing SitePoint Premium Collections (and Our Best Price Ever)%


Source: Sitepoint

The Importance of Writing Code That Humans Can Read

Have you ever finished a project in a single run without ever needing to look at the code again? Neither have I. When working on an older project, you probably want to spend little to no time figuring out how code works. Readable code is imperative to keep a product maintainable and to keep yourself, and your colleagues or collaborators happy.

Exaggerated examples of unreadable code can be found on JS1k contests, where the goal is to write the best JavaScript applications with 1024 characters or less, and JSFuck, an esoteric programming style which uses only six different characters to write JavaScript code. Looking at code on either of these sites will make you wonder what is going on. Imagine writing such code and trying to fix a bug months later.

If you surf the internet regularly, or build interfaces, you may know that it’s easier to quit a large, bulky form than one that seems simple and small. The same can be said about code. When perceived as easier to read and to work on, one may enjoy working on it more. At least it will save you tossing out your computer in frustration.

In this article, I’m going to look at tips and tricks to make your code more readable, as well as pitfalls to avoid.

Code Splitting

[author_more]

Sticking with the form analogy, forms are sometimes split in parts, making them appear less of a hurdle. The same can be done with code. By splitting it up into parts, readers can skip to what is relevant for them instead of ploughing through a jungle.

Across Files

For years, we have been optimising things for the web. JavaScript files are no exception of that. Think of minification and pre-HTTP/2, we saved HTTP requests by combining scripts into a single one. Today, we can work as we want and have a task runner like Gulp or Grunt process our files. It’s safe to say we get to program the way we like, and leave optimization (such as concatenation) to tools.

// Load user data from API
var getUsersRequest = new XMLHttpRequest();
getUsersRequest.open('GET', '/api/users', true);
getUsersRequest.addEventListener('load', function() {
    // Do something with users
});

getUsersRequest.send();

//---------------------------------------------------
// Different functionality starts here. Perhaps
// this is an opportunity to split into files.
//---------------------------------------------------

// Load post data from API
var getPostsRequest = new XMLHttpRequest();
getPostsRequest.open('GET', '/api/posts', true);
getPostsRequest.addEventListener('load', function() {
    // Do something with posts
});

getPostsRequest.send();

Functions

Functions allow us to create blocks of code we can reuse. Normally, a function’s content is indented, making it easy to see where a function starts and ends. A good habit is to keep functions tiny—10 lines or less. When a function is named correctly, it’s also easy to understand what is happening when it’s being called. We’ll get to naming conventions later.

// Load user data from API
function getUsers(callback) {
    var getUsersRequest = new XMLHttpRequest();
    getUsersRequest.open('GET', '/api/users', true);
    getUsersRequest.addEventListener('load', function() {
        callback(JSON.parse(getUsersRequest.responseText));
    });

    getUsersRequest.send();
}

// Load post data from API
function getPosts(callback) {
    var getPostsRequest = new XMLHttpRequest();
    getPostsRequest.open('GET', '/api/posts', true);
    getPostsRequest.addEventListener('load', function() {
        callback(JSON.parse(getPostsRequest.responseText));
    });

    getPostsRequest.send();
}

// Because of proper naming, it’s easy to understand this code 
// without reading the actual functions
getUsers(function(users) {
    // Do something with users
});
getPosts(function(posts) {
    // Do something with posts
});

We can simplify the above code. Note how both functions are almost identical? We can apply the Don’t Repeat Yourself (DRY) principle. This prevents clutter.

function fetchJson(url, callback) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.addEventListener('load', function() {
        callback(JSON.parse(request.responseText));
    });

    request.send();
}

// The below code is still easy to understand 
// without reading the above function
fetchJson('/api/users', function(users) {
    // Do something with users
});
fetchJson('/api/posts', function(posts) {
    // Do something with posts
});

What if we want to create a new user through a POST request? At this point, one option is to add optional arguments to the function, introducing new logic to the function, making it too complex for one function. Another option is to create a new function specifically for POST requests, which would result in duplicate code.

We can get the best of both with object-oriented programming, allowing us to create a configurable single-use object, while keeping it maintainable.

Note: if you need a primer specifically on object-oriented JavaScript, I recommend this video: The Definitive Guide to Object-Oriented JavaScript

Object Oriented Programming

Consider objects, often called classes, a cluster of functions that are context-aware. An object fits beautifully in a dedicated file. In our case, we can build a basic wrapper for XMLHttpRequest.

HttpRequest.js

function HttpRequest(url) {
    this.request = new XMLHttpRequest();

    this.body = undefined;
    this.method = HttpRequest.METHOD_GET;
    this.url = url;

    this.responseParser = undefined;
}

HttpRequest.METHOD_GET = 'GET';
HttpRequest.METHOD_POST = 'POST';

HttpRequest.prototype.setMethod = function(method) {
    this.method = method;
    return this;
};

HttpRequest.prototype.setBody = function(body) {
    if (typeof body === 'object') {
        body = JSON.stringify(body);
    }

    this.body = body;
    return this;
};

HttpRequest.prototype.setResponseParser = function(responseConverter) {
    if (typeof responseParser !== 'function') return;

    this.responseParser = responseParser;
    return this;
};

HttpRequest.prototype.send = function(callback) {
    this.request.addEventListener('load', function() {
        if (this.responseParser) {
            callback(this.responseParser(this.request.responseText));
        } else {
            callback(this.request.responseText);
        }
    }, false);

    this.request.open(this.method, this.url, true);
    this.request.send(this.body);
    return this;
};

app.js

new HttpRequest('/users')
    .setResponseParser(JSON.parse)
    .send(function(users) {
        // Do something with users
    });

new HttpRequest('/posts')
    .setResponseParser(JSON.parse)
    .send(function(posts) {
        // Do something with posts
    });

// Create a new user
new HttpRequest('/user')
    .setMethod(HttpRequest.METHOD_POST)
    .setBody({
        name: 'Tim',
        email: 'info@example.com'
    })
    .setResponseParser(JSON.parse)
    .send(function(user) {
        // Do something with new user
    });

Continue reading %The Importance of Writing Code That Humans Can Read%


Source: Sitepoint

JavaScript Beyond the Web in 2015

2015 has been a big year for the Internet of Things. We have seen huge advancements in the size and capability of devices, big players like Microsoft and Samsung are really moving into the space and the IoT community overall is starting to grow ever larger! Over the past two years here at SitePoint, it has become a bit of a tradition for me to look at the year that was for the Internet of Things and JavaScript (see JavaScript Beyond the Web and JavaScript Beyond the Web in 2014). Whilst the initial hype and excitement of having JavaScript as a language of the Internet of Things (IoT) seemed to calm down a bit over 2015, JavaScript still continues to pop up as a rather strong option for enabling magic within more IoT platforms than people realise.

In this overview, we’ll look at some of the big movements in the Internet of Things that will enable new possibilities for JavaScript developers and further JavaScript’s potential beyond the web.

Tessel 2

The Tessel 2 (Photo credit: Tessel)

The Tessel 2 (Photo credit: Tessel)

The Tessel is a microcontroller (similar to an Arduino) that ran on JavaScript rather than the typical languages such as C. It was the perfect device to help JavaScript lovers leap into the Internet of Things. Last year it shipped to the world and people made some pretty neat things with it. The Tessel 2 released pre-orders this year and has some very exciting upgrades from the first generation Tessel.

The Tessel was able to run various npm packages, but wasn’t able to run Node.js itself, so Tessel often had to build in compatibility specifically for commonly used packages. The Tessel 2 greatly improves on this by running the real Node.js out of the box. This fact alone made me pre-order it instantly. Access to npm modules brings a lot of potential to this microcontroller.

It also has two USB ports, providing access to USB devices (e.g. webcams) as well as ready made Tessel modules and the GPIO port (to directly connect all sorts of electronics via jumper wires to pins).

Getting Involved Via JavaScript

  • Pre-order Tessel 2 – Pre-order the Tessel 2 from their website and start planning your creations! The Tessel 2 should arrive in January 2016.
  • Official Tessel Documentation – You can start reading up on Tessel development already (Tessel 1 development should be very similar to Tessel 2).

Open Hybrid

OpenHybrid in action (Photo credit: OpenHybrid)

OpenHybrid in action (Photo credit: OpenHybrid)

Augmented reality is a fascinating alternative method of controlling the Internet of Things around us. Rather than putting physical controls on objects, you can view them through an augmented reality interface like a smartphone app and control them in intuitive and limitless ways! Various companies are looking into ways of implementing this but in 2015, MIT Media Labs revealed (and open-sourced) a pretty impressive method called Open Hybrid. JavaScript IoT developers in particular might be very interested by this solution as it allows for application development via web technologies including HTML and JavaScript. Whilst it is still early days for augmented reality, now is the time to start tinkering with its potential alongside the IoT!

Getting Involved Via JavaScript

Samsung’s IoT.js and JerryScript

The Samsung IoT.js and JerryScript pages

The Samsung IoT.js and JerryScript pages

Samsung has put plenty of resources towards enabling JavaScript to be the language for the Internet of Things. In 2015, they open sourced JerryScript, a JavaScript engine for the Internet of Things. It allows JavaScript to run on small, resource constrained devices like the microcontrollers commonly used in the IoT. To me, it sounds similar to what Tessel were attempting to put together in the first iteration of the Tessel but on a grander scale which is open to many more small IoT devices.

IoT.js is another endeavour of Samsung to enable JavaScript within the Internet of Things ecosystem. It was open sourced around the same time as JerryScript. IoT.js is a framework for creating an inter-operable platform for devices using JavaScript. It has been described as a lightweight version of Node.js, however I’ve yet to play around with it myself to get a true feel for how accurate that description is.

Both JerryScript and IoT.js are still in their early stages, so it will be exciting to see how they progress throughout 2016. I’m eagerly hoping for integration with the Samsung SmartThings platform at some point but I haven’t heard of any mention of this yet!

Getting Involved Via JavaScript

  • Official JerryScript Page – The official page for JerryScript contains links to downloading the engine and guides on getting started.
  • Official IoT.js Page – The official page for IoT.js also has a download link and guides for getting started.

Continue reading %JavaScript Beyond the Web in 2015%


Source: Sitepoint

Filling out PDF Forms with PDFtk and PHP

PDF files are one of the most common ways of sharing documents online. Whether we need to pass our clients’ documents to third-party service providers like banks or insurance companies, or just to send a CV to an employer, using a PDF document is frequently the first option.

PDF files can transfer plain/formatted text, images, hyperlinks, and even fillable forms. In this tutorial, we’re going to see how we can fill out PDF forms using PHP and a great PDF manipulation tool called PDFtk Server.

To keep things simple enough, we’ll refer to PDFtk Server as PDFtk throughout the rest of the article.

Digital document illustration

Installation

We’ll use Homestead Improved for our development environment, as usual.

Once the VM is booted up, and we’ve managed to ssh into the system with vagrant ssh, we can start installing PDFtk using apt-get:

sudo apt-get install pdftk

To check if it works, we can run the following command:

pdftk --version

The output should be similar to:

Copyright (c) 2003-13 Steward and Lee, LLC - Please Visit:www.pdftk.com. This is free software; see the source code for copying conditions. There is NO warranty, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

How It Works

PDFtk provides a wide variety of features for manipulating PDF documents, from merging and splitting pages to filling out PDF forms, or even applying watermarks. This article focuses on using PDFtk to fill out a standard PDF form using PHP.

PDFtk uses FDF files for manipulating PDF forms, but what is an FDF file?

FDF or Form Data File is a plain-text file, which can store form data in a much simpler structure than PDF files.

Simply put, we need to generate an FDF file from user submitted data, and merge it with the original PDF file using PDFtk’s commands.

What Is Inside an FDF File

The structure of an FDF file is composed of three parts: the header, the content and the footer:

Continue reading %Filling out PDF Forms with PDFtk and PHP%


Source: Sitepoint

Grab This Three-in-One Charging Solution for $25

Plug your phone into the USB port on your laptop and you’ll have hands-free, eye-level access to it while it charges, thanks to the Bobine’s 24-inch, ultra-strong and flexible cable. And it’s that strong cable that makes it a great tripod for taking photos. Crafted from military-grade, nickel-plated steel, the Bobine is virtually indestructible, no […]

Continue reading %Grab This Three-in-One Charging Solution for $25%


Source: Sitepoint

Desktop Wallpaper Calendars: December 2015


  

Since seven years, our monthly desktop wallpapers post is a Smashing favorite that wouldn’t be possible without the tireless efforts of designers and artists from across the globe. Each month, we challenge you, the design community, to get your creative juices flowing and produce some interesting and inspiring desktop wallpapers. And well, we are very thankful to everyone who tickles their creativity and contributes to this challenge every month.

Desktop Wallpaper Calendars: December 2015

This post features artwork for December 2015. The wallpapers all come in versions with and without a calendar and can be downloaded for free. Now it’s up to you to decide: which one will deck your desktop this month?

The post Desktop Wallpaper Calendars: December 2015 appeared first on Smashing Magazine.


Source: Smashing Magazine

Creating an Interactive Video Showcase with the Video API

This article was peer reviewed by Tom Greco and Marc Towler. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

With support for the native video element and its API being fairly comprehensive, now is a great time to look at ways in which you can leverage video to create something fancy and interactive for your users.

Throughout this article we will look into the basics of the API and after we get our bearings we will work on a practical example. This will be a multi-video slider in which videos are played one after the other, seamlessly loading and animating in content as they play.

Video API – a Brief Introduction

In essence when we talk about the Video API we are really talking about the Media API — a JavaScript API that allows you to interact with audio and video elements on a web page.

[author_more]

This API implements an interface called HTMLMediaElement which adds the properties, methods and events needed to support basic operations common to audio and video (such as loading media, changing the seek position, finishing playback etc). It is extended by both HTMLVideoElement and HTMLAudioElement which provide special properties and methods of their own.

We are mainly concerned with the video element so we will be focusing on interactions with this part of the API. To get a feel for the API you can visit the Interactive HTML5 Video Example page that showcases the most commonly used elements.

Screenshot of HTML5 Video Example page

Browser Support

While Chrome, Firefox, Safari, Opera and Internet Explorer all support the video element, they differ in the formats they can play, each browser has a list of video formats it supports which may also be based on the version of the browser itself.

At the time of writing, all modern desktop and mobile browsers will play the mp4 format. In addition most browsers will have long support for either (or both) theogg or webm formats. Here is a comprehensive overview of the current state of support.

While you can get by with just supplying the mp4 version you should probably include both the ogg and webm formats as a comprehensive solution.

An Interactive Video Showcase

We will be creating a showcase feature using the video element. Our showcase will play a series of small video clips back to back and trigger animations at certain times. Using the video element like this we investigate some of its properties, methods and events and show the level of control you can achieve using this API.

Screenshot of finished showcase

As ever, you can find the code for this tutorial on our GitHub repo, as well as a demo of the finished showcase at the end of the article.

Structuring the HTML Layout

Our slider example will play each of the videos one after the other, fading in the related captions for each video as we play. If we don’t support video playback / are on a mobile device we will fallback to a static image will caption text.

Create outline wrapper for your slider and inside of it add each of the video sections you want to define

<!--Main video wrapper-->
<div class="video-wrapper" id="video-container">
  <!--first video-->
  <div class="video-container"></div>
  <!--second video-->
  <div class="video-container"></div>
  <!--Nth video-->
  ...
</div>

Markup for Each Video Section

For each of the videos we want to play, we will have to set up a few elements so that we can display the video, fade in the caption elements and track the video’s overall progress.

<!--Progress bar-->
<div class="progress-bar">
  <div class="progress">
    <div class="progress-inner">
      <span class="progress-time"></span>
      <span class="progress-value"></span>
    </div>
  </div>
</div>

<!--Progress bar overlay-->
<div class="progress-overlay"></div>

<!--Video Elements-->
<video preload="none">
  <source src="videos/video1/video1.mp4" type="video/mp4">
  <source src="videos/video1/video1.webm" type="video/webm">
  <source src="videos/video1/video1.ogg" type="video/ogg">
</video>

<!--Video overlay-->
<div class="overlay"></div>

<!--Caption Elements-->
<div class="caption">
  <h1 data-animation-percent="10">Amazing New Adventures</h1>
  <h3 data-animation-percent="20">Come visit new parts of the world</h3>
  <p data-animation-percent="40">
    Don't wait, there is a wide world out there that you can explore!
    Contact us to see what we can do
  </p>
  <div class="readmore" data-animation-percent="60">Find out more</div>
</div>

The video elements will contain the video tag with all of it’s source children set to different data types to maximize compatibility. Read more about adding support for multiple video formats.

The caption elements will contain all the markup you want to fade in as the video plays. You can add anything you want but it must have the data-animation-percent attribute with a value between 0 and 100. This will tell the slider on which percentage of video completion to fade the element in.

The progress bar displays the current video progress (in both seconds and percentage). This will be updated periodically. The bar will be interactive; When you hover over it, the video will pause and you will be able to seek through it, updating the video position.

Providing a Fallback for Mobile Browsers

To help cover all of our bases we will also set up a fallback element that will be used in case the current browser is a mobile device.

Add the following markup just before the end of main video wrapper

<!--Fallback-->
<div class="fallback-container">
  <div class="image"></div>
  <div class="overlay"></div>
  <div class="caption">
    <h1 data-animation-percent="15">This is a title</h1>
    <h3 data-animation-percent="25">Fallback when you dont support autoplay!</h3>
    <p data-animation-percent="50">Come and see a wide range of tasks and activities</p>
    <div class="readmore" data-animation-percent="70">Act now!</div>
  </div>
</div>

This will function in a similar way to our videos. When the user loads the page will we will trigger all of its caption elements to fade in depending on the value set in the data-animation-percent attribute. Instead of a video we are using a background image.

Detecting Mobile Browsers

Our slider will be using the autoplay attribute of the video element to automatically play the first video. However, most mobile browsers block this functionality (so as to limit data usage) meaning that the best policy is to detect if we are on a mobile device and display the fallback content accordingly. Normally we would do this using feature detection, however, detecting support for the autoplay attribute is tricky.

As ugly as browser / UA sniffing may be, it will give us a way to test the current browser against a known list of mobile devices and from there detect how we should proceed. We can do this using the following regular expression:

var mobile = navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|IEMobile/i);

Which we can then use like so:

if(!mobile){
  //main video slider functionality
} else {
  //fallback functionality
}

Current and next Video Setup

Note: For the JavaScript functionality I’m using jQuery, so go grab yourselves a copy and include it at the bottom of the page.

Once we are happy we’re not on mobile we search for all of our elements with the video-container class and assign the collection to our videos variable. These elements will be the main wrappers for each video and its caption content. We then find each container’s video element and assign it to the local video variable.

Now that we have our current video, we need to find the next video element so that we can trigger it to start loading and play it when the current one has finished.

If we have another video-container element we assign that to the nextVideo variable. If we don’t have another video-container element we loop back to the first.

var videos = $('.video-container');

videos.each(function(index){
  var video = $(this).find('video'),
      nextVideo;

  if(index !== (videos.length - 1)){
    nextVideo = $(this).next('.video-container').find('video');
  } else {
    nextVideo = videos.first().find('video');
  }
});

You can add as many video-container elements as you want, and define additional videos and captions. The slider will find the next video and create a loop so that it continues indefinitely.

Continue reading %Creating an Interactive Video Showcase with the Video API%


Source: Sitepoint

A Tour of Python Collections

In this tutorial, I’m going to cover several different types of collections in Python.

Before we get started, let’s define what a collection is. A collection is similar to a basket that you can add and remove items from. In some cases, they are the same types of items, and in others they are different. Basically, it’s a storage construct that allows you to collect things.

For example, you might have a car type. You create several instances of the car and want some way to group all of those cars together and access them easily. This is the perfect scenario for a collection.

The collection will survive in memory. You don’t need to build the collection or create any type of scaffolding. All of that is provided for free. Just create a collection instance and start adding your cars. When you’re ready, you can pull them out by name or by index (position within the collection).

Python offers several built-in types that fall under a vague category called collections. While there isn’t a formal type called collection in Python, there are lists, mappings, and sets.

In this tutorial, we’re going cover the following types:

  • lists
  • strings
  • dictionaries
  • sets

List

Lists in Python are a built-in type called a sequence. List are mutable and allow you to add items of the same type or different types, making them very versatile constructs. Python lists are similar to arrays in other languages.

Python lists do allow you to have non-unique elements.

The following is an example of creating a list and adding items to it:

alist = ["item1", "item2", 4]

Notice the list is heterogenous, containing string and numeric types.

To retrieve an item from a list, simply reference the item’s index. Python lists are zero indexed. If I want the last item, which is position 3, I need to use an index of 2:

alist[2]
> 4

The number 4 is returned. When referencing a list item, just subtract one from its position to get the correct index value.

Checking the length of a list can be done using the len command:

len(alist))
> 3

To add more items to the list, use the append() function:

alist.append(False)
len(alist)
> 4

We’ve increased the list by one and added a different type — the boolean. The list doesn’t complain at all.

We can delete elements by calling remove():

alist.remove("item2")

remove() doesn’t return a value. The list will be updated and now contains three items:

['item1', 4, False]

There are a couple of other ways to get items out of a list. We saw how to access an item using its index. If I access index 2, I’ll get item 3:

thevalue = alist[2]
print(thevalue)
> False

The above code will supply us with a copy of the item. That item is still in the list. The overall list count isn’t affected.

However, if we use pop(), we get the item, but it is also removed from the list:

thevalue = alist.pop(1)
print(thevalue)
> 4
print("after pop", alist)
> ['item1', False]

Lists can also be sorted. If I have the following list of strings:

alpha = ["z", "b", "a", "c"]

you can sort it using the sort() command:

alpha.sort()

sort() doesn’t return a value. However, alpha is now sorted. You can see this by printing the list:

print(alpha)

Elements can be reversed just as easily by calling reverse():

alpha.reverse()

reverse() also doesn’t return a value, and will reverse the current list.

Continue reading %A Tour of Python Collections%


Source: Sitepoint

Modeling an Aggregate with Eloquent

The Aggregate pattern is an important part of Domain Driven Design. It prevents inconsistencies and is responsible for enforcing business rules within a collection of objects. For these reasons alone, it is clear to see why it is a key component of a domain model.

Architectural advice recommends that the layer containing the Domain Model be independent from infrastructural concerns. While this is good advice, the Active Record pattern on the other hand wraps a row in the database. Because of this, it is almost impossible to decouple from the persistence layer.

Mixing persistence concerns into a Domain Model can become complex and lead to a lot of bad decisions. This does not mean that it is impossible to create an Active Record Domain Model. In this article, we will work through an example of building an Aggregate which also extends Eloquent: a popular Active Record ORM.

What is an Aggregate?

An Aggregate is a collection of objects which act as a single unit – with one of these objects acting as the Aggregate’s Root. Interaction from outside of the Aggregate must only communicate through the Root object. Aggregate Roots can then manage the consistency of all the objects within its boundary.

A set of elements joined into one

Boundaries for Aggregates define the scope of a transaction. Before the transaction can be committed, manipulation to the cluster of objects must comply with the business rules. Only one Aggregate can be committed within a single transaction. Any changes required to additional Aggregates must be eventually consistent, happening within another transaction.

In his book, Implementing Domain-Driven Design, Vaughn Vernon outlines a set of guidelines in which he calls: “the rules of Aggregate design”:

  1. Protect True Invariants in Consistency Boundaries
  2. Design Small Aggregates
  3. Reference Other Aggregates Only By Identity
  4. Use Eventual Consistency Outside the Consistency Boundary

Continue reading %Modeling an Aggregate with Eloquent%


Source: Sitepoint