Magento 2 Development Fundamentals: Creating and Extending Classes

Peter Ford | February 20th 2018

First off the use of classes / object oriented programming (OOP) is not specific to Magento or even PHP, but it is a core concept that needs to be understood if you want to work successfully with Magento.

What is a class?

A class is a template or blueprint for an object (object is what we call an instance of a particular class), for example “car”, “van” and “motorbike” are all types of Vehicle they all share certain properties such as colour and number of wheels, therefore we could create a class of Vehicle where we define the properties we expect a Vehicle to have. So how does this look in actual code?

 
1
2
3
4
5
6
7
8
9
10
11
12
13
Class Vehicle
{
    public $colour;

    public $numberOfWheels;

    public function __construct($colour, $numberOfWheels)
    {
        $this->colour = $colour;
        $this->numberOfWheels = $numberOfWheels;
    }

}

To create the class we use the keyword Class followed by the class name, in this case Vehicle.

Inside the curly braces we have defined a couple of variables or properties (property is the term used when referring to values stored inside classes). As with any other variable these can be anything we like; strings, booleans and even other objects are all fine. For our example let’s create  $colour (string) and $numberOfWheels (integer), don’t worry too much about the public keyword for now, that is used for defining where this property is available.

The next line we have a function called __construct  with the parameters $colour and $numberOfWheels passed into it, __construct is what is know as a magic method and it is called whenever a new instance of the class is created, heres an example:

1
$car = new Vehicle("red", 4);

Here we are creating an instance of the class Vehicle, by passing the string “red” and the integer 4 as parameters the __construct method will set these as the values for the properties $colour and $numberOfWheels respectively. We are then able to access these properties in the following way:

To access within the the class file:

1
$this->colour;

To access a value once the object has been created, we’ll our car object as an example:

1
$car->getColour();

(this method will only work if the property is public)

We can also create functions within our class for example:

1
2
3
4
5
public function getColour()
    {
        return "Your Vehicle is " . $this->colour;
    }
}

Functions which are associated with objects are called Methods.

The simple method getColour() will just return a string which reads “Your Vehicle is “ and then whatever the value of $colour is, so, for the $car object we created earlier it will look something like this:

1
echo $car->getColour();

Which will output “Your Vehicle is red”. Simple!

Our complete Class looks as follows (Vehicle.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Class Vehicle
{
    public $colour;

    public $numberOfWheels;

    public function __construct($colour, $numberOfWheels)
    {
        $this->colour = $colour;
        $this->numberOfWheels = $numberOfWheels;
    }

    public function getColour()
    {
        return "Your Vehicle is " . $this->colour;
    }
}

And our index file (index.php) where we created the instance $car

1
2
3
4
5
include "Vehicle.php";

$car = new Vehicle("red", 4);

echo $car->getColour();

Extending a class

So we have our class Vehicle now imagine we wanted to add a new property called sidecar, sidecar is specific to only one kind of vehicle which is a motorbike, so what are our options? We could add the sidecar property to the original Vehicle class but this would be bad practice as we know only one type of Vehicle will use this property, instead what we will do is create a new class called Motorbike.

We still want all the methods and properties that are in Vehicle but we also want to add new ones, we can do this by extending Vehicle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
include_once "Vehicle.php";

class Motorbike extends Vehicle
{

    public $sidecar;

    /**
     * Motorbike constructor.
     * @param $sidecar
     * @param $colour
     * @param $numberOfWheels
     */
    public function __construct(
        $sidecar,
        $colour,
        $numberOfWheels)
    {
        $this->sidecar = $sidecar;
        parent::__construct($colour, $numberOfWheels);

    }
}

(Motorbike.php)

You can see that the way we define the class is similar, we still use the Class followed by the class name, however when we are extending a class we also need to define what class we are extending, we do this by simply adding extends + ‘parentClassName’.

In our Motorbike class I have defined another property $sidecar, which will be a boolean for true this motorbike has a sidecar or false it doesn’t. We need to add this property to our constructor so it gets a value assigned when the class is instantiated (term for creating a new instance of a class). Again this is similar to before, we pass in $sidecar as a parameter to the __construct function, then inside the function we set the value of $sidecar to the value which is passed when a new instance of the class Motorbike is created.

You will notice we haven’t defined the properties $colour or $numberOfWheels again as they already exist in the parent class, however we do need to include them in our __construct method as we want them to be assigned a value when this class is instantiated, to do this we need to call the parent __construct method, which we do as follows:

1
parent::__construct($colour, $numberOfWheels);

So the complete __construct method for the Motorbike class looks like:

1
2
3
4
5
6
7
8
9
public function __construct(
        $sidecar,
        $colour,
        $numberOfWheels)
    {
        $this->sidecar = $sidecar;
        parent::__construct($colour, $numberOfWheels);

    }

We assign $sidecar a value the same way we did the properties in the parent class.

Lets create a new instance of the Motorbike class called $bike:

1
$bike = new Motorbike(true, "blue", 2);

(index.php)

We can now access properties and methods in $bike that exist in either the Motorbike or Vehicle classes:

(Sidenote, the properties/methods we can access where depend on what Visibility they have set, in our examples they are all public so will be available in any object that uses or extends these classes.)

1
2
3
echo $bike->hasSidecar();

echo $bike->getColour();

hasSidecar() is a method defined in the Motorbike class, whereas getColour() is defined in the parent Vehicle class.

Applying This To Magento

Quite often we will need to add functionality to an existing Magento class, for example, let’s say we’ve created a module called TestModule, inside this module we want to create a new block with a class that extends the core Magento class \Magento\Framework\View\Element\Template

The first thing we need to do is create a new file inside Vendor/Module/Block, so for this example we have Pushon/TestModule/Block/Test.php

1
2
3
Namespace PushON\TestModule\Block;

class Test extends \Magento\Framework\View\Element\Template

Namespace is the file path to your block file, so in my case Pushon\TestModule\Block then the name of the class matched the name of the file the class lives in so the class Test is defined in the file Test.php, for the extend we use the full path to the class, in this case(starting in the vendor folder)  Magento\Framework\View\Element\Template.

Next our constructor, we need to include the properties from the parent class as we want them to be assigned a value when our new class is instantiated, (remember including $colour and $numberOfWheels in our Motorbike class constructor).

To see what properties are included in the parent class __constructor we must go to where the class is defined, in this case, vendor/magento/framework/View/Element/Template.php if we look inside the constructor (~ line 143) we see:

1
public function __construct(Template\Context $context, array $data = [])

So we need to pass an object with the class context, $context,  and an array, $array into the __construct in our own child class:

1
2
3
4
5
6
7
public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        array $data = []
    )
    {
        parent::__construct($context, $data);
    }

Notice the longer file path to define the $context in our constructor, this is because in Template.php it was starting from the folder that the file was already in (Element), whereas we need to define the path from the Vendor folder. Finally remember to call parent construct method to assign values to $context and $data.

We should now be able to create a new block with our new class, so lets try it.

We’ll add a template to the homepage with some simple text in it:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="PushON\TestModule\Block\Test" name="test" template="test.phtml" before="-" />
        </referenceContainer>
    </body>
</page>

Here we add a new block called test to the homepage of our site. The block uses our new class and inside the template we will have an object called $block. $block will contain the properties and methods which have been defined in our class and it’s parents.

To demonstrate, let’s create a simple function called helloWorld inside our Test class:

1
2
3
4
public function helloWorld()
    {
        return "Hello World";
    }

And inside our template file we can call the function by:

1
echo $block->helloWorld();

This should output the string “Hello World”!

Finally what if I want to only show this message if the user is logged in, well we need to add some extra functionality to our class:

We can do this by injecting another core Magento class, $session into our constructor.

1
2
3
4
5
6
7
8
9
10
11
protected $session;

    public function __construct(
        \Magento\Customer\Model\Session $session,
        \Magento\Framework\View\Element\Template\Context $context,
        array $data = []
    )
    {
        $this->session = $session;
        parent::__construct($context, $data);
    }

At the top we define a new property, in this case it is $session, then we add this to our __construct method so it is given a value when our class is instantiated, so $session will be assigned an instance of the class \Magento\Customer\Mode\Session. We are then able to use properties/methods from the $session class in our own template:

Our helloWorld function becomes:

1
2
3
4
5
6
7
public function helloWorld()
    {
        if ($this->session->isLoggedIn() === true) {
            return "Hello Logged In User!";
        }
        else return "Hello User";
    }

Where we are using the method isLoggedIn() on the session object to return the current users logged in status.

Our final Test class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Namespace PushON\TestModule\Block;

class Test extends \Magento\Framework\View\Element\Template
{
    protected $session;

    public function __construct(
        \Magento\Customer\Model\Session $session,
        \Magento\Framework\View\Element\Template\Context $context,
        array $data = []
    )
    {
        $this->session = $session;
        parent::__construct($context, $data);
    }

    /**
     * @return string
     */
    public function helloWorld()
    {
        if ($this->session->isLoggedIn() === true) {
            return "Hello Logged In User!";
        }
        else return "Hello User";
    }
}