Simple Binding

You say to the container that when you want to resolve an instance of DatabaseWriter class, follow this logic I just told you in the closure coz I know better. Every single time that you want to resolve the class, you must follow this and deliver me a new instance.

You use this type of bindings all the time. You're giving the container small recipes on how to make your things for you.

Singleton Binding

Same as simple bindings, with one obvious difference. You're telling the container that I want only one instance of this class across my whole application. The first time you're resolving the class, follow the logic in the closure I passed to you, but be sure that you just return that only instance every other time you want to resolve it. Deliver me the only instance you were allowed to make.

It's a singleton, right? Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container.

Obviously, you use this type of binding when you want to utilize the Singleton pattern. It's rare these days.

Instance Binding

It's like doing a favor for the container. You don't tell her how to instantiate a certain class, you do it yourself and just give her the instance. She holds it for you and returns it on subsequent calls into the container.

It's especially handy when you're unit-testing. If you bind a mock instance to the container for some class, all the subsequent calls to app()->make() will return that mock for you. So you're practically injecting a class mock all over the app when the actual class is used.

Primitive Binding

Laravel's container provides a DSL for you to tell her how to resolve primitives as well. You say when BillingController class wants a $taxRate variable and it's not passed, give it 0.2. It's like setting default values from far far away!

Interface Binding

It's used when you want to bind interfaces to concrete implementations.

After reading a dozen articles on SOLID and how to be a better programmer, you decide to follow Dependency Inversion principle and instead of depending on concrete instances, you depend on abstractions.

After all, it's a good practice, in or out of Laravel.

class DatabaseWriter {
protected $db;
// Any concrete implementation of this interface will do
// Now, that I depend on this DatabaseAdapterInterface contract,
// I can work with MySQL, MongoDB and WhatevaDB! Awesome!
public function __construct(DatabaseAdapterInterface $db)
{
$this->db = $db;
}
public function write()
{
$this->db->query('...');
}
}

Without Laravel's container, you first need to create a concrete implementation of DatabaseAdapterInterface and pass it through DatabaseWriter's constructor to be able to instantiate it:

$dbWriter = new DatabaseWriter(new MongodbAdapter)

If MongodbAdapter has its own dependencies, you might end up here:

// Looks familiar, right?
// These are those recipes you used to give to Laravel container
// through simple binding.
$dbWriter = new DatabaseWriter(new MongodbAdapter(new MongodbConnection))

But with Laravel's container in the party, you tell her that when anyone asks for a concrete implementation of DatabaseAdapterInterface, ask no more and give them a MongodbAdapter:

app()->bind(DatabaseAdapterInterface::class, MongodbAdapter::class)

Then you go on and resolve an instance of DatabaseWriter out of container, like a boss:

$dbWriter = app()->make(DatabaseWriter::class)

Much easier and cleaner, right? You remove all the obvious clutter and move it to somewhere else. Your AppServiceProvider maybe.

OK, let's see how she works in this scenario. First, she probes DatabaseWriter for possible dependencies (through reflection), sees that it needs a DatabaseAdapterInterface. Checks her notebook, recalls that you told her that MongodbAdapter is the concrete implementation of that interface. Makes one and hand it over to DatabaseWriter.

You use these type of bindings almost all the time, if you're adhering to dependency inversion principle.