Magento Tutorial For Beginners, Full Course (2024)

Mark teaches Magento 2 from scratch, covering the LEMP stack, modules, routing, controllers, and dependency injection. You'll build a simple module with routes, controllers, templates, and translations, learning core concepts like factories, layout XML, and viewmodels along the way.
Chapters
Are you ready to dive into e commerce development with Magento 2? If you're nodding your head, you found the right place. Hey there. I'm Mark, a Magento programmer and teacher, and I'm so excited that you want to learn Magento programming. Just like many of you, I was super overwhelmed when I got started with Magento. It was just so extremely confusing to me and these giant guides just left me with a ton of terminology and concepts that I didn't understand at all. It felt like I was blindfolded, tasked with trying to solve a puzzle written in another language that I didn't understand. I eventually learned Magento, but it took me years. I did this by breaking these seemingly random, large, complex concepts down into simple and easy to understand, bite sized lessons and I put them in an order that made logical sense for those new to Magento. I don't want you to go through the same pain and struggle that I went through. This Jumpstart course isn't meant to be all inclusive, but rather a quick course to get your feet wet into some of the introductory concepts of the framework once you want to learn more. I have a collection of over 500 video lessons totaling over 30 hours of premium content over at McAdemy so you can learn all of the foundational aspects of Magento programming once and for all. So are you ready? Let's get started. And if you like these free lessons, be sure to give this video a thumbs up to help spread the word to others who may also be interested in learning about Magento. Good luck. Magento is an open source e commerce platform that is a highly extensible and modular framework. It's definitely more advanced than some other open source platforms out there, but with it comes powerful customization capabilities. Magento runs on the LEMP stack which is Linux, Nginx, MySQL and PHP, along with a few other pieces of tech. Let's go over the main layers. Linux is the operating system which offers excellent security, is highly stable, and supports virtually all major programming languages, including php. NGINX is the web server that handles incoming web requests and then forwards them to other parts of the stack, notably PHP for processing. It's a much more modern web server than Apache and is known for its high performance and low memory usage, which aligns really well with the demanding needs of a framework like Magento. MySQL is the database server that is by far the most widely used and popular open source database management system. MySQL is used by Magento to store all types of data related to the storefront, including products, customer data, order data and much more. PHP is the backend server language that is the most widely used language on the web. Magento heavily utilizes all of PHP's object oriented features in the framework such as dependency injection and interfaces. XML is a semantic language that is used to configure and manipulate data. Magento uses XML for creating new functionalities and extending existing ones. HTML is a subset of xml, so you are probably already familiar with its basic syntax. Elasticsearch is a powerful database server that powers the search engine capabilities of Magento. It provides shoppers with robots rapid search results that are much more accurate than other database servers such as MySQL and Redis is another open source database which utilizes an in memory data structure to function both as a cache and a message broker. Magento uses Redis for caching as well as session management and also helps Magento scale over multiple servers. These services were not chosen at random. They were carefully selected by the Magento Core Core architecture team because they work really well together to fulfill the needs of the demanding Magento framework. Flexibility, performance and security were the core aspects in choosing these services and building on top of open source frameworks creates a really extensible platform that can be fully customized without the need to worry about software licensing, subscriptions and so on. While Adobe Commerce is the paid enterprise version of Magento, it shares the exact same core code base as Magento. Open Source Commerce is basically a license based software agreement that is composed of a collection of premium third party modules built by Adobe that lay on top of Magento Open Source. Access to SaaS, offerings such as Adobe Commerce Cloud as well as support directly from the Adobe team are two of the main selling points of Commerce. But Adobe Commerce is not a requirement to build a highly performant and available storefront because the core that powers Adobe Commerce is really powered by the Magento Open Source Core. This is the basic underlying structure of the services that power both Magento Open Source and Adobe Commerce. The usage of Open Source at its core provides the extremely powerful base to build a fully extensible ecommerce storefront without any dependency on third party services. This architecture lays the groundwork for every other part of the application, including the file system and directory layout, which we'll explore in the next lesson. It's time to dive into Magento's directory structure after you install Magento. If you just look around at the files and folders, things can seem daunting. Looking beyond the initial top level directories, Magenta Magento has over 100,000 files and folders. This large number of directories stems from Magento's extensibility and flexibility features. The amount of folders exist purely to organize the code base. You actually won't be dealing with most of them, but it's still important to know what the purpose is of each of these folders. If nothing else than to make you aware of what's going on at a high level, let's look at the most important directories, which are App, Pub, var and Vendor. The app directory contains custom modules and themes that you'll be building. The app code directory is for modules and App design is for themes, and the App Etc directory contains a few more configuration files that make Magento run properly. It may surprise you that app code is empty, but that's just because this is a new project worry. It's totally normal because we haven't started coding anything yet, so you may be wondering how Magento functions. If this directory is empty, that's where the vendor folder comes into play. This directory contains all third party modules installed with Composer, including Magento core code. This is considered an ephemeral directory, which means that it is somewhat temporary. You shouldn't really ever place files within this directory. That's what the app directory is for. When Magento is deployed to staging and production servers, the vendor directory may be completely rebuilt from the configuration defined within the Composer JSON file in the root of your install. So we won't be placing files here that are meant to stick around for a long period of time. Let's head over to the vendor Magento directory. If we scroll down within this directory, we will see many directories that start with a module dash. These are the modules that are related to Magento, and if we go ahead and open one up, for example Module Catalog, which is a pretty popular module, you'll see some common subdirectories such as block, controller, model, and so on. Magento uniformly uses naming conventions and ensuring files are predictably placed. This is particularly crucial when navigating Magento's extensive file system and all of these files and folders and let's scroll up a bit and go back to vendor. Magento Framework this is where the Magento framework resides. The Magento framework isn't considered a module, but a separate set of core PHP classes and interfaces that provide the foundational functionality for building Magento modules. This directory contains code for things like routing, dependency injection, the data layer, events, plugins, caching, logging, configuration, and many other lower level components. These classes don't directly implement functionality, but they provide an API and tools for modules to build on top of, much like any other files within the vendor directory. You'll never be modifying any of these files however, you will frequently reference them when building out your custom modules. Let's go ahead and head over to the Pub directory. This directory is known as the webroot, which is the main directory that the web server points to rather than pointing to the index PHP file in the root of your install. It's common practice to segregate web requests within a public directory. Segregating web requests enhances security by ensuring that they can't access private files in the file system. Abiding by this best practice, Magento designates the Pub index PHP file as the actual web entry point for Magento, so this file does some initial bootstrapping and then kicks off the Magento app. This setup means that the web server can't access static assets like images or CSS unless they reside within this Pub directory. Magento uses Pub static as the main directory that contains the CSS and JavaScript for requested files from the server, and images typically reside in the media directory. You'll notice that this directory is empty though, and that's because these files are generated from source files located in App design and vendor, and this is part of a process called static content deployment. This just means that these files are generated from a script that runs during code deployments, and keeping it separated like this will decrease page loading speeds and provide some extra functionality for your Magento install. Let's head over to the Var directory. This directory contains generated data that changes often, such as cache files, logs, and session files. This is a truly ephemeral directory and is just meant to store transient data. It's best practice to have a separate directory like this for this temp data. And finally, there are a few other directories within the root of our Magento instance. These other folders aren't really used on a day to day basis, so you should pretty much breathe a sigh of relief that you really only need to worry about these few main folders that we talked about. While these important directories do form the core, feel free to explore the rest of the file system, including the Bin directory which contains the Bin Magento CLI tool, the Dev directory for unit tests, the Lib directory for other frameworks, and the Setup directory which contains some install scripts. But don't get overwhelmed with Magento's giant code base. Knowing about App, Pub, Var and Vendor Window will usually be enough to get you through about 95% of your daily Magento duties. There are a few tools that you can use to increase efficiency during Magento development. Let's step through a few of my favorites. We'll start with phpstorm. Phpstorm is a very popular integrated development environment or IDE for php. I prefer it because it notably speeds up my magento 2 development comes with intellisense built in has access to a variety of plugins including new AI tools that can help you write code faster such as GitHub, Copilot, and it also has many built in tools that will really increase the quality of your code. You can familiarize yourself by checking out the phpstorm website. Note that if you also enroll in any of my paid offerings, you'll also get a six month license of phpstorm completely free. Next is the Magento PHPSTORM plugin. This plugin adds many Magento specific functionalities and checks to phpstorm. It understands the Magento structure, detects Magento modules, helps with XML configurations, and navigates through the many Magento classes that you'll be writing in your own code. It's my favorite plugin for phpstorm because it's tailored specifically for Magento development and since it's built by Adobe, you can be sure that it will be supported for many years to come. Next is xdebug xdebug is a PHP extension that really helps you debug code on another level. It provides tons of valuable insight into what code is actually doing behind the scenes, and it allows you to interactively debug PHP code as your scripts execute. Nothing beats being able to step through your code like line by line to see exactly how it's being executed and how data is being manipulated. You can learn more about it and check out the full documentation@xdebug.org Next is one of my favorite tools which is Magento Cache Clean. Cache Clean is a must have for every Magento developer. Built by longtime Magento developer Vinai Cop, it watches your Magento 2 installation for changes and automatically cleans out the affected cache types in Magento. You'll be cleaning the cache a lot during development, so this is a massive time saver when working on templates, layout, XML and other Magento related files. You can learn more about it over on GitHub. And finally, my last favorite tool is a shameless plug for one of my own tools which is Docker Magento. Docker Magento brings the power of containerization to Magento development. Built by yours truly, it offers all of the services needed to run a Magento application in isolated Docker containers, all without not needing to know anything about Docker Docker Magento also makes it extremely easy to replicate your entire Magento application stack across teams as well as across different environments. It also includes built in support for phpstorm, xdebug, and Cache Clean, so you can get right into Magento development right away. You can learn more about my Docker Magento project on GitHub, and if you actually check it out and scroll down, you'll see that there's a link to a completely free course that I have available that that teaches you all about Docker Magento. This free course will walk you through every step of setting up a new Magento development environment, and if you haven't set one up yet, be sure to do so now because we're going to start getting into Magento code next. The naming conventions for modules in Magento are straightforward. A module name consists of two parts, a vendor name and a module name. In this course we'll use macademy as the vendor name and Jumpstart as the module name. Typically, a module name describes what it does. For example, if we were creating functionality for a blog, we would just name it Blog. But since we will just be creating some test code, Jumpstart works since it's related to this course within our app code directory, let's go ahead and create a new directory named macademy. Vendor names and module names will always start with a capital letter and then within that directory let's go ahead and create another one named Jumpstart. Remember, casing is extremely important in Magento, so just be aware of that when you create these folders. Next, we will create another directory within our module name and this one will be named Etc or Etc. This directory contains XML configuration files for our module. Magento uses this interesting XML approach to make code flexible and more maintainable, and you'll find out a bit more about this later. But first, within this Etsy directory, let's go ahead and create a file named module xml. This file lets Magento know that our code is in fact module code. It could be hard to remember the exact format for these files, which is why I'm going to use my tool Beeline to to generate the root node of this file for me. I'll do this just by typing M2 module root. If you don't have access to Beeline, just tap the View Source Code button below this lesson to grab the code. You can also copy and paste the contents from another module XML file into your custom file. Again, don't worry about remembering this file or the format. Even after programming in Magento for so many years, I still refer back to other modules for examples of these files and node format. Now we will simply create a new module node. So you'll notice this looks just like HTML, so there's nothing crazy going on here. It will have a single attribute name which will be set equal to our vendor name and module name, but combined with an underscore. So this will be macademy underscore jumpstart. Magento uses this vendor name underscore module name syntax all throughout its internal code to keep track of what's happening within your module. This name should exactly match the names of your module directory names, casing, spelling and all. And finally, we can just close up this node. Now we need to make Magento aware of our module on the PHP level. So we'll do that by going back to the root module directory and creating a new file named registration.php. and again, I will just populate this file with my Beeline tool just to save some time. But going over this, we are declaring strict types, which is just a good practice to put in all of your PHP files. And then we will use the component registrar class from the Magento framework. We'll call its register function statically and then refer to the content module type, which is just a string of module, our component name, which is our vendor name, underscore module name format as well as the directory to register these files with, which is the root of our Magento module. And that's it. Now that we have created our Magento module, all we need to do is enable it with command line. So we can do this by running bin slash magento module colon enable m academy underscore jump start. And again you'll see this format of this vendor name, module name string everywhere in Magento. And we get a message that says our module has been enabled, but they haven't yet been registered. It tells us to run setup upgrade, but that's only really needed if we have other files in this module or database setup scripts. Otherwise we can also just run bin slash magento cache flush. And we'll do that because it's a little quicker than running setup upgrade. And by flushing the cache, this will ensure our module is properly registered with Magento. We can confirm this is the case by running the command bin slash Magento module status. The output of this command will show our module in the list of enabled modules. If it doesn't, be sure to double check the spelling and casing of Your module name in your directory names and your code files. Congrats. You've just made your first Magento module. It wasn't that scary, right? But it doesn't do anything yet besides just making itself aware to the Magento code base. Each extra piece of code added to our module from this point on carries out very specific functionality. Don't worry if it feels overwhelming at any time. Remember, we're just learning it step by step. Let's look at a URL in Magento. For example this create an account link. When you look at the URL for this page, you'll see a URL that looks something like this. This Magento follows a model view controller design pattern, also known as mvc. The controller part of that pattern in Magento follows a very structured format as each of the parts of the URL help decide where to route requests. These three main parts of the URL make up the front name, controller name and action name and form the core of Magento's URL routing pattern. So in this case, Customer is the front name, account is the controller name, and create is the action name. Let's understand how this routing mechanism works by creating our own front name Controller name Action name Combo. The front name is the start of the request and relates back to a specific module. Route files are defined with xml, but we must also specify the area of the route. Magento has two main areas, Front end and admin HTML. The front end deals with the front end instance of Magento and admin HTML deals with anything admin related. Since we want to respond to requests on the front end of our Magento instance, we'll create a new directory within etc and this will be named front end. Then let's create a new file with in this front end directory and this one will be named routes xml. This will again follow a specific format for that root config node. So I will again use Beeline to generate it for me. Within it we will define a new router node and we will use the standard router. There are a few different router types in Magento, but we are just dealing with the standard router for now. Within it is a related route node and this will accept two attributes. The first is id. Let's say we want to create a new URL at slash jumpstart. So we will name this route ID jumpstart and we will tack on another front name attribute and this will also be named jumpstart. And this route node accepts a module node and this is the module that will be responsible for for responding to request made to slash jumpstart. This will have a name attribute set equal to the module that is responsible in that special string format. So this will be macademy underscore jumpstart and that's it. Our route is now all set up and we can now proceed to create a controller to respond to these requests. We are listening for requests at slash jumpstart, but we also need to define the controller name and action name. If these aren't defined at all, they will default to index index. So our request made to jumpstart slash index slash index is the same as a request made to just slash jumpstart. So we need to create that controller name and action name. Part first is the controller name. This name corresponds to a class within a controller folder of your modules. Let's go ahead and create a new directory named Controller. And please make sure it starts with a capital C and doesn't end in an S. Those are common mistakes. And within this directory we will create another directory and this will represent that second part of the URL, which is the controller name. Again, Magento uses Index by default if that part of the URL is not present. So let's go ahead and create a directory named Index again with a capital first letter. Our next part of the URL, that third part is the Action name. This refers to the specific class that will be responsible for handling this request and will live within our Controller name folder. Again, if this part of the URL is not present, Magento defaults it back to Index. So let's go ahead and create a new file with an index. And this will be named Index PHP again with a capital first letter. And this will be a PHP tag. And let's go ahead and declare strict types. And this will reside in the namespace of macademy Jumpstart Controller Index. If you aren't that familiar with PHP namespaces, it's just a way of organizing your classes and code. In Magento, this is pretty simple. It's always the vendor name, module name, and then the location underneath that module name. So Controller is here and Index is here. So it maps pretty simply and uses backslashes to delineate each part of the namespace. Next will be the class name, which will just be Index. And this class will implement HTTP Get Action interface from Magento Framework App Action. Older versions of Magento extended another class named App Action Action. This is no longer recommended because Magento now favors composition over inheritance, which is just a fancy way of saying they recommend you now implement Interfaces rather than extending classes. Implementing interfaces ensures our code includes all of the functions defined in an interface, and it also aids in building reliable and dependable code. For example, we can see that phpstorm is showing us a red squiggly and and it says that the class must be declared abstract or implement method execute so we can go ahead and add those method stubs which is just the execute method and this is just a standard public function. So this is the function that will execute when someone visits slash jumpstart. For now, let's go ahead and just die out some text that says jumpstart just to keep things simple and make sure our script and routing is working properly. Now I do have the cache clean module running in the background, but if you don't you will need to flush the cache to ensure that your routes XML file is picked up. You really only need to flush the cache anytime you change or add something related to xml, and you can do that just by running bin magento cache flush and after that's flushed we can go ahead and visit slash jumpstart, wait a few moments for the caches to be rebuilt and we get our jumpstart text returned. So this lets us know that all of the routing is properly set up and our endpoint is returning a response if a 404 error page is displayed. Instead, just double check the naming and spelling of all your files and make sure they are all in the correct location. Dependency injection sounds like a very complicated term, but when we break it down it's really quite simple. So let's rewind and cover the absolute basics of what dependency injection is before we learn how it works. In Magento, a PHP class is a blueprint for creating objects. If you aren't familiar with this concept, be sure to check out my PHP101 course for an introduction to classes and object oriented programming. In php, a class can rely on one or more other classes to perform its functions. The standard way to create implementations of these objects is with the new keyword. For example here in this product model class which creates a new category object within the constructor with the new keyword. When we use the new keyword to create an instance of a class, that instance is created within its own class scope. This means that it is created just within this specific class. This practice decentralizes the responsibility of creating objects across different classes in our code. This isn't necessarily an issue, but it can lead to tightly coupled code. It also makes your code less flexible and when it comes to unit testing, more difficult to isolate so what's the alternative? Well, rather than using the new keyword within constructors, we we can have a new single class that is responsible for the creation of these objects. This design pattern is known as the dependency injection container. In the context of Magento, we call this container class the object Manager. It's a very good name because it's responsible for the creation and management of all objects in the framework. This means that every time an object is created, the object manager class is the class that is responsible for creating it. By establishing a central class that is responsible for the creation of all of these objects, this approach significantly enhances the power and flexibility of a framework. Not only will our code now be more flexible and easier to test, but it will also be much more extensible because objects are now created in one place. This allows us to change the implementation of any class at any time by swapping in a different implementation. We will explore this in much greater detail in a later lesson. With dependency injection, things become a bit simpler. We'll actually remove the new keyword. Since the object manager is now responsible for the creation of these classes, we will also move the reference to this class, which is category, in to the constructor as an argument, and then we will assign it to a variable name such as category. We can then assign that value to the class property. Now, when this product class gets used and instantiated, the constructor is executed and the arguments defined within the constructor are automatically passed through to the object manager. Don't worry too much about how this happens. Magento has already implemented this for us without getting into the lower level mechanisms that make this work. Be aware that an instance of the category class is being created in the background and then returned and assigned to this category variable. This code works exactly the same way as it did before. This is much more powerful though, because if the category class also had any dependencies, which is almost always the case with classes in Magento, those dependencies will also automatically be created with the object manager and. And so on, all the way down the tree. The core idea behind the Magento object manager is that you don't directly create objects in your own classes. Instead, you specify your requirements within the constructor, and the object manager provides the instance of the class you need. In practice, you should never make explicit calls to the object manager class. Instead, you'll lay out your dependencies within your classes in the constructor arguments just like this, and the object manager will take care of the rest. To sum things up, the object manager is a very powerful tool in Magento that helps you stick to proper object oriented programming practices. By discouraging the use of the new keyword, Magento takes a step forward, ensuring your code is more testable, modular and manageable. That's all there is to dependency injection. Understanding these basic principles can help demystify the complexity around this topic. Since every version of Magento 2.4.4 and onwards supports PHP8, we can take advantage of PHP8's constructor property promotion feature. You'll notice that there is currently a lot of repetition going on here. We are defining our private property named category, which is defined up in the property definition, in the constructor argument, and again in the property assignment and the variable name. We are also referencing the type that the property is assigned to and the type that is created with dependency injection. This common pattern of defining a property an argument and then assigning it within the constructor is so common that the PHP core developers created Constructor Property Promotion which greatly simplifies this code. This is going to be pretty neat, so watch what I can do here. I can move this scope of the property in to the constructor and that's it. Now I can remove this reference within the constructor and also the property assignment above it. Isn't this pretty wild? This approach performs the same function as before, but with much less code. Since there are typically more than one dependencies injected into constructors as arguments, it's generally best practice to go ahead and add line breaks to all of the constructor arguments. And we could go ahead and tidy up this constructor body so it looks just like this. And we can also add on a trailing comma in php8 which can really cut down our version control diffs in code reviews. I consistently use Constructor Property promotion when writing any new Magento code, and I recommend you using it as well due to the many benefits it provides. If you haven't used interfaces before, they can be a bit mysterious until you understand why they should be used. An interface in PHP is a contract for a class. It defines what methods a class should have without providing any actual implementations for these methods. That's instead the responsibility of the class which implements this interface. A key point to remember is that a class implementing an interface must implement all of the methods defined in the interface. This enforces a level of consistency and integrity. Let's look back at this product class. This code relies on the category class rather than relying on this class, though it should instead rely on an implementation of this class. So instead of relying on the category, we will instead instruct Object Manager to rely on an implementation of its interface. So let's rename just this class reference to category interface. This will be a new interface that we haven't yet created, so let's go ahead and create a reference for it with use McAdemy Jumpstart API category Interface it's best practice in Magento to place all interfaces within an API directory. And looking at this product class, we will see that the category calls a function called get name. So at the very least our interface needs to require the creation of this method. Here's how we will create this interface. First let's create that API directory in the root of our module and then within it we will create a new PHP file and this will be named CategoryInterface PHP. Let's open up our PHP tag and go ahead and declare strict types. This lives in the namespace of macademy jumpstart API and if I page down notice that phpstorm is auto completing this for me. That's because I have GitHub Copilot enabled. Since Magento is complex, GitHub Copilot doesn't always work, but in this case it's working perfectly. We will define our interface named Category interface. So just like how we define classes with class and then the class name, we will define interfaces with interface and then the interface name and interfaces just define the signature of functions that are required to be created. Notice how there are no open and closed brackets in this file. We just have the visibility of the function, the function keyword, the name of the function, and then the return type. So by defining this function we can be sure that any class that implements this interface must implement the getname function and it must have a return type of string. Now that we have an interface defined for the category, our category class must implement this interface. So let's go ahead and go to our definition of class category. Let's add on an implements keyword and we are implementing the category interface. This ensures our class abides by this interface and we can see this in action. If we go ahead and remove this get name definition, we'll see that phpstorm is complaining that the class must be declared abstract or implement the method get name. This means that interfaces enforce classes to implement specific functions and matching return types. If we go back to our product class, we will see that we already have a reference to this category interface. But Magento does not yet know which class to use when it comes across this interface. In the next lesson we'll use some XML to assign this using something called a class preference XML plays a crucial role in Magento 2 when it comes to dependencies. At the moment when Magento comes across this category interface, it doesn't know which implementation of the interface to use. We can instruct object manager to use the category class as the implementation. Whenever category interface is invoked, this is referred to as a class preference. We tell Magento which class we prefer and it will use this to determine which class to substitute in for the interface. At runtime, we will do this within a DI XML file and this file will be stored within our. Etc directory. So let's go ahead and create a new file named di XML and that of course stands for dependency injection. Feel free to use any code generator to generate this code, or you can grab this root config node from any other di XML file in Magento. And notice that the no namespace schema location is set to the object manager and within this config node comes a preference node. So let's go ahead and open a bracket and define the preference. And this preference accepts two attributes for and type. This XML document essentially says whenever you come across this class or interface, use this class type. So four will be a reference to our interface. So this will be macademy Jumpstart API category interface. So this is a class preference for this interface. And when you see this interface, use this class and this will be macademy jumpstart model category. So again we are defining a class preference for this interface to use this class. And that's it. Our changes will be applied as soon as the cache backend is cleared. The code can now rely on an interface instead of a class, and as long as that interface is obeyed, any class that implements that interface can be swapped in and swapped out. This is referred to as a service contract. Magento uses a service contract based architecture where an interface defines all of the capabilities that a module can offer. This reduces the coupling between modules and makes it easier to tweak or replace the behavior of a module. Implementing the interface ensures that all instances of the category class provide a consistent method to retrieve the category name. But here's where interfaces really shine. Suppose in the future you decide to change the way category names are fetched. Maybe instead of just hard coding a string, here you want to pull out data from a database or call a third party module. As long as the changes don't alter the type of data that is defined in the interface, which is a string, you can modify your category implementation without breaking anything in the rest of your application. Any part of your system Using the category interface will work just fine as long as it gets the data that it expects. For example, this product class is calling the category interface and it is relying on git name. So as long as the gate name function returns a string which abides by the interface definition, everything will work properly. Implementing interfaces in your Magento 2 classes ensures your code is extensible and maintainable. It also allows third party developers to easily tweak or modify an implementation while retaining compatibility with other code. Third party modules can define their own own di XML files and class preferences to override your preferences. This is why the XML layer is used in Magento to swap in and out these dependencies. In a previous lesson we learned how to create a basic controller for a module which simply terminates the PHP script execution and echoes out a string. While this was great for learning in the real world, we wouldn't want to just stop the execution of a script in the middle of our application. Instead, we need to create a page response that fits in perfectly with Magento's layout system. Magento has a class that we can use which allows us to render a blank empty page which we can then build on top of. First we will go ahead and create a constructor in our index controller action. We'll do this by typing public functionconstruct and then we can go ahead and define our dependencies. Within the constructor. We will define a private property and then we will use dependency injection to inject the page factory object from Magento Framework View result and lastly we will assign it to a property named page factory. A factory is one of those special types of objects that is usually built and managed by Magento's Object manager. When we use Factory classes to create objects, Object Manager will also provide a brand new instance of the object each time it's requested. This differs from the default functionality of having it always return an instance of the same object, which is also known as a singleton pattern. Factory objects are a bit unique compared to singletons because we need to call their create method after they're instantiated to actually create a new instance of that object. Let's go ahead and replace this die statement within our execute function and we will just return an instance of a call to this page factory create. This calls create on the factory class to return an instance of the non factory object. This means that calling create on factory objects always returns an instance of the root object. This means that the return type on this execute method is actually a page object from Magento Framework. View result rather than an instance of the factory. So this is what our full class looks like after it's all been typed up. Now, when we head to the front end of our site and refresh the page, we should see an empty Magento page as a response just like this. Since we didn't get any errors, this tells us that our code is working properly. Creating a standard blank page is an important step in Magento module development. Since the response aligns perfectly with Magento's layout system, we can now start easily adding in custom functionality and content to this empty page. Magento uses a special file extension for template files that use the p HTML extension. You can think of phtml files as PHP files that mainly output HTML. But rather than needing to use standard PHP for loops, command conditionals, or any other rendering logic, Magento's phtml format supplies some syntactic sugar to display this presentational data. This adds an easier way to call this PHP logic in these template files. This represents the V of Magento's MVC layer, which is the view. All files related to a specific Magento module in this presentational layer are located in a module's view directory. So within our root of our module, let's go ahead and create a directory named view. Within this directory we can again choose either front end or admin HTML whether we are dealing with the front end of Magento or the admin area. Since we are strictly dealing with the front end, we will create another directory in view named frontend. And all of these phtml templates reside in another directory of the subdirectory named templates. This gives us the full path of view front end templates. And within this templates directory is where we will create our phtml files. Let's create a new file named welcome phtml in this welcome phtml file we can then add the HTML that is going to be displayed for this template. Later on we'll dive a bit into how to get data into this template, but for now let's just output hello world within an H1 tag. So let's go ahead and open up an H1 tag and then spell out hello world. And to have consistent line breaks on all of the different operating systems, I always my files with a line break and that's it. Our template file is now all created, but Magento won't yet display it because we haven't yet told it where to render to. Rendering is when the code actually gets turned into output that is then displayed on the screen. That's where layout XML comes in, which we'll cover next. Magento distinguishes itself from most other open source frameworks because it uses XML to manage the layout configuration of a page. This provides significant extensibility, enabling third party modules to easily override the layout within the view front end directory. Let's go ahead and create another directory and this one will be named Layout. Similar to templates, a layout directory can also be created in a VUE admin HTML directory when creating an admin layout configuration file. Remembering back from our lesson on URL anatomy, we dissected the URL into a front name, controller name, and action name. Note that the URL of Jumpstart contains just a single part of the URL which is the front name. The controller and action name default to index. So our jumpstart route is the same as jumpstart index index and that renders the page exactly the same as jumpstart. The combination of front name, controller name and action name is also reflected in the naming of layout XML files, with each part being distinguished by an underscore. Magento uses this naming convention to build the layout configuration for every page that is rendered. So utilizing what we just learned within our layout directory, let's create a new file and this will follow that naming convention. So this will be named JumpStart,_index,_index.xml. Again, this follows the same naming convention as front name, controller name and action name in the URL, but instead of the parts being distinguished by slashes, they are distinguished by underscores. Let's create this file and next we'll start building out the XML for this file. The jumpstart index index XML file comes with a root page node which you can generate with your tool of choice. To save some time, I'm going to go ahead and use my Beeline tool to generate this root element. You can also extract this node and its format from any other Magento Layout XML file Layout XML files contain a body node. This represents and stands for the body of a page. Within it usually comes a reference container node that refers to an existing container element that has already been added to the layout someplace by another module. Containers in Magento are structural elements used to contain other containers and blocks. Let's go ahead and add in a reference container node and we will set its name attribute equal to content. This references the main content container, which is the main component that contains all other elements in a standard Magento page layout. Next we will create a block node. All blocks should have a name, and we'll define this using the Name attribute. I usually like to prefix my module blocks with the vendor name and module name and I always follow a lowercase snake case format. So let's name this macademy jumpstart and then comes the name of the block and this one will be called welcome. And note how I am delineating the names with underscores. This is just my preference. You can also use periods or something else. Just be consistent. And our block note should refer to the template that it will output using the template attribute. The value of this attribute follows a very specific syntax. It's vendor name, underscore module name. So in this case this will be macademy jumpstart and note how we are capitalizing each letter of this module name vendor name and this will be followed by two colons and then the name of our template relative to the view front end templates directory. So this will be welcome phtml and finally we can just self close this tag. To go over this really quickly, we are referencing the container content and just adding in this custom block and we are telling Magento to use this template when rendering this block. After making changes to Layout xml, you also need to flush the cache with bin slash Magento cache flush. And let's head back to the front end of our site and refresh the page and if all goes correctly, we will see our hello World being outputted to the screen which is the contents of our welcome phtml file. As you develop more complex Magento stores, your clients will reach wider audiences. For this larger group of users, English may not be their first language. To account for this and to create a better user experience for your customers, it's crucial that we translate the strings in our template files. This would allow strings such as hello world to be translated into other languages. Magento has made it really simple for us to translate these strings by program providing a PHP translation function which we can pass our strings through. Let's go ahead and open up a short echo tag and then we will pass in our string to this echo tag and then finally we will wrap it in that special translation function. This is denoted with a double underscore and the first argument passed to it is the string that we want to be translated. When it comes to writing strings in template files, it's always best practice to pass all text through this translation function. This allows Magento to find all of these translatable strings and replace them with the corresponding translated text from a translation CSV file. Wrapping our strings in the translation function now allows these strings to be translated Magento uses comma separated values CSV text files for providing this translation capability. They are stored in a directory named i18N, which is an abbreviation for the term internationalization. And I just found out the 18 stands for the number of letters between the first I and the last n in that word. Pretty neat. So let's go ahead in the root of our module. So in our jumpstart directory, let's go ahead and create a new directory named i18n. And then within it we will create a new CSV file. And this will be named after the locale that we want to translate strings to, for example, French. The locale is FR underscore capital F capital R. So that's what we will name this file and then always add the extension.CSV. now, in this file we need to implement the translation. To do this, we will replace hello world with Bonjour lamond. Hopefully I'm saying that right? To write translations in a CSV file, we follow the format original text, translated text. And it's always a good idea to wrap your text in double quotes just to make sure Magento finds and translates them properly. And you also need to make sure the original text exactly matches its appearance in the module or template, including spaces and punctuation. So going back to our template file, we used hello world here with an exclamation and a lowercase w. So let's go ahead and add this into that CSV and then we will translate this to Bonjour le monde. And then we can of course, go ahead and save this file. And now if a user's locale is set to French and you have a French store view, they will see Bonjour le mans instead of the hello world text. So that's pretty neat. I don't currently have a store view set up for French, but we can also use translation files to do something else, which is simple text changes. And we'll look at that right now. So it's also possible for us to use translation strings to easily change out text strings. This is really useful if you just want to change the text of a module or a template file, and you don't want to override the entire file. So just like this French translation file, we can create a file with our current locale code. And since we are on an English store view, this will be en us in caps dot CSV. Now we can use the exact same format as we did in our French translation file, but write our text update in the second column. So this will be hello World and then in the second column we will output the text that we want displayed instead. For example, hi there. Now when we go ahead and refresh our page, we will see this updated text text and not the hello world text. Again. This could be really useful if you want to override text in a third party module or theme. And it's why it's so important to always pass text through translation functions, even if you aren't translating your store to other languages. Remember, just as before, if you use this format, the value that you place in the CSV file must exactly match the string as it is written in the module or template file spaces and all. I've previously mentioned that Magento's architecture is MVC or model view controller, but it's more accurately described as MVVM or MVVMC for Model View. Vue Model Controller the viewmodel provides a way to abstract away complex logic that may make its way into the presentation layer. Viewmodels in Magento are essentially PHP classes that are used to implement the presentation logic in a dedicated class separate from block classes. These are differentiated from block classes because they offload non UI logic. In other words, a viewmodel class lives in an intermediate layer between the model and the view, and it helps manage the data specifically related to the view. As you may know, it's considered a bad practice to have PHP logic in Vue scripts. Prior to Magento 2.2 blocks were often used for this purpose, running the risk of them getting cluttered and incomprehensible over time. That's where viewmodels come in. They offer a way to create clear, logical chunks of code that are easier to follow, debug, and can be kept in separate classes from the blocks. Viewmodels are also extremely reusable. Once a viewmodel is written, it can be used across many view scripts, making them both a powerful and efficient tool to use in the presentation layer of Magento. It's important to remember that a viewmodel does not replace a block. The block is still in charge of rendering the template. The viewmodel exists to handle the presentation logic to make it more manageable and organized. Essentially, the viewmodel prepares the data that is then displayed in a template file. A single block can have access to multiple view models, and this makes the system more flexible and helps to keep our code more modular and less dependent on other classes. Remember that every bit of complexity that we can abstract away will bring us one step closer to creating cleaner and more efficient code. Now let's Learn how to implement a viewmodel in a block so we can then call it from within our template. We just learned about the purpose and use of viewmodels in Magento's MVVMC architecture. Now we're going to put that knowledge into practice and integrate a viewmodel into a block of the Magento layout. We will start by creating a view model for our macademy Jumpstart module. Let's say that we wanted to display a welcome message to our customers based on the time of day, which would be some pretty advanced logic that would make our template more complex than it needs to be. Because of this, this logic to determine the message to be displayed will be stored in a viewmodel first within the root of our module. So within our jumpstart directory, let's go ahead and create a new directory named View Model and within it we will create a new class named welcome Message php. Let's go ahead and open up our PHP tag. We will declare strict types and this will be in the namespace of PHP macademy Jumpstart View Model. This class will be named welcome Message and this will implement the argument interface from Magento Framework View element Block it's important that you explicitly implement this argument interface as it's required by Magento to allow our class to be used as an argument in in layout XML files. Next, we will create our function that we will call from our template. So let's create a new public function named Get Welcome Message. And to save some time, I'm just going to paste some logic in here that detects the current hour of the day and returns a different message depending on its value. And I can see phpstorm complaining that it's missing a return type and it's actually expected to be of type Magento Framework phrase which is returned when we call the translation function. And that's all a viewmodel is. It's simply implementing Argument interface and then we write any functions that we want to call from our template. Now, viewmodels won't be automatically available within our template, so we need to declare it in layout XML and pass it as an argument to the block. So we need to go where our template is being added to the layout, which is in this jumpstart index index XML file. And rather than closing up this block tag, let's go ahead and open it up so we can pass in some arguments to it. These will be defined within an arguments node and it can accept any number of arguments. Now, since our view model is named welcome Message I will name this in all lowercase and snake case. Welcome Underscore message and I always like to suffix my viewmodel names with underscore View Underscore model Since there can be many arguments passed to blocks, it's just a good thing to call these out as viewmodels so we know exactly what they are. We also need to define the type of argument this is, and we can do this with another special attribute named xsi colon type and we will set this equal to object and then finally within the value for the argument we will pass in the fully qualified class name to our view model. This will be M Academy Jumpstart View Model Welcome Message and there we go. Now we have access to this welcome Message class by calling the argument name of welcome Message viewmodel from within our block. So let's add back to our template file and before any of this output let's go ahead and create a PHP open enclose tag so we can actually just write some PHP in this phtml file. Let's create a new variable name and this will be named welcome Message View model and we will assign it to the value of this view model. Now to reference the block that this template is currently within, we have access to a special variable and this is named block. Now this represents the block that we are actually within, which is this block node within the content container. This allows us to access content within the block, such as arguments and such as our viewmodel. Let's copy the name of this viewmodel argument, go back to our template and we will call get data and then pass it a string to this argument name. Now note that phpstorm is complaining that it can't find the variable block, but we can just add a type hint. So phpstorm knows how to reference this variable. We could do this by starting what looks like a multi line comment and then a space and then the name of the class. Well, note that it created it for welcome Message viewmodel. So let's make that reference first so we can reference that welcome message view model directly from the stock block in much the same way we can create another one for that block variable and this will reference magento framework view element templates. And now you can see that that red squiggly went away and and if we command click into the class we can find the exact calls that are going on within this block. Now we can retrieve that custom response of get welcome message within the contents of our template. So let's go ahead and replace this call of hello world and call our welcome message viewmodel. Get welcome message and note that we also have to flush the cache for our updates to take effect since we changed some layout xml. Now if we head back to the front of our site and refresh cross our fingers and yes we are getting the response. Good night. So it looks like everything's working. So we know now that Magento will automatically display and change the welcome message depending on the current time of day. Congrats. You've successfully separated the logic from the view, so this organizes complex logic away from the view, making your template files much simpler and only concerned with the code relating to the presentation of data. Congrats on making your way through to the end of this course. Remember, it just barely scratched the surface of programming in Magento, but hopefully it inspired you to want to learn more. If so, check out my Magento 2 Code Coding Kickstart course, which expands on all the topics covered in this Jumpstart course. It contains lessons covering so many more concepts not covered in this course and covers them in much greater detail. Visit M Academy Kickstart to learn all about the course and enroll in it. Thanks so much for watching this video. If you found it at all useful, remember to tap that like button to show your appreciation. And until next time, keep coding.










