Claudiu Persoiu

Blog-ul lui Claudiu Persoiu


Archive for the ‘Magento’ Category

How I use Magento2 on my local with Docker and Docker Compose

without comments

Disclaimer

Please note that this solution is tailored specifically for my needs and, while your needs may vary, don’t worry, everything is on GitHub, so feel free to take what you need.

I would like to add that this is not a “you’ve been using Docker with Magento2 wrong, this is how it’s done” kind of blog, I just want to share what I’m using and how. It may not be the best fit for you, but maybe you will find something useful.

Intro

For almost 2 years I’ve been using Magento2 in Docker containers. I’ve been doing that before, but I must admit that it was because I had to, not because I’ve seen the light, I mean advantages.

As you may know Magento2 is not exactly a small and light app, it’s quite heavy on the resources, especially during development.

Compared to a VM, with Docker you get:

  • Speed: I think the speed is one of the biggest advantages, you can stop and start containers very fast, only the first build will take time, after that it will be very fast;
  • Light on resources: Compared to a VM, the container does not need to include the entire operating system, so it will not take a lot of space on disk and will not use a lot of processing power, because it’s not an entire OS doing… well… OS stuff, it’s just a server most of the time.

What you don’t get:

  • Learning curve: if you don’t know Docker and Docker Compose, it will be less intuitive at first;
  • First setup: harder to setup at first, if you have been using a VM for a long time, you will feel that you are going against the tide, but I assure you, in the long term it will be a lot simpler this way.

Taking the above into consideration, I would like to say that when I’ve started with this setup I was using Linux with 8G of RAM. One of my colleagues even wished me good luck on installing Magento2 on a ultraportable 8Gb RAM system. He wasn’t even sarcastic, more like pitying me for my bad workstation selection.

One of the requirements was that I needed some isolation and configuration between projects, I couldn’t just install a server and be done with it.

Previously I’ve been using Vagrant and VirtualBox, a great fit, very easy to use (most of the time). However, for Magento2 I’ve realised that it was heavy enough on its own, it was making me run out of resources fast.

Also, I wanted it to be easy to use, I don’t like to have to remember and type out a 3 word command, I just want to press some tabs and get it over with.

The requirements

There were some specific requirements:

  • nginx config – should work out of the box, Magento configuration isn’t very small, I wanted to make use of it with ease;
  • SSL – the domain has to also work with HTTPS, mostly because some APIs require it, the certificates don’t need to be valid;
  • bash – the Magento command should work as the system user, not as root (as containers usually do). This is required, because I don’t want the files generated by Magento to be generated as root (and therefore only removable with root rights);
  • xdebug – must work out of the box and be easily integrated with an IDE.

The implementation and usage

Magento2 offered a Docker container to work with. I will not say anything about it, since it wasn’t at all something I needed.

My main source of inspiration was: https://github.com/markoshust/docker-magento. The project changed a lot since I’ve started, so I definitely think you should check it out.

The starter point is: https://github.com/claudiu-persoiu/magento2-docker-compose

The relevant files are:

  • magento2 – it should contain a folder html with the project
  • dkc_short – it can reside anywhere, but it should be added to the files ~/.bash_profile or ~/.bashrc, this file contains shortcuts, it’s not necessary, but I like it because it make my life easier;
  • docker-compose.yml – it contains all the mappings and relevant containers.

NOTE: I think I should point out that the commands on the PHP container run in two ways, as the system user or as root. This is a limitation of the Linux implementation, please make a note of it, as I will refer to it later.

Step 1:

What you should do when starting a new project with an existing Magento2 repository:

$ git clone https://github.com/claudiu-persoiu/magento2-docker-compose.git project_name
$ cd project_name
$ git clone your_own_magento2_repository magento2/html

Step 2 (optional):

Copy the shortcuts to your bash console:

$ cp dkc_short ~/
$ echo ~/dkc_short >> ~/.bash_profile
$ source ~/.bash_profile

NOTE: If you don’t have the file ~/.bash_profile on your computer, just use ~/.bashrc

Step 3:

Start the setup:

$ dkc-up -d

It will take a bit of time the first time, but it will be a lot faster next time you run it.

Step 4:

Run composer install:

$ dkc-php-run composer install

That’s about it.

What is this dkc stuff?

Well, I like to use tabs when running a command, so I added some aliases that allow me to run a Magento command without typing everything, I just type dkc[tab]p[tab]-[tab] and the command. I just love bash autocomplete.

The command list is very simple:

  • dkc-up -d – start the containers in the background
  • dkc-down – stop all containers
  • dkc-mag [command] – run a Magento2 command
  • dkc-clean – clear the cache
  • dkc-php-run – run a bash command inside the php container, like composer in the previous example. NOTE: This command is running as the system user, not as root.
  • dkc-exec phpfpm [command] – this is same as above, but running as root. You should almost always use the command above.
  • dkc-exec [container] [command] – this command needs a bit more explanation:
    • container can be:
      • app – for Nginx server,
      • phpfrm – for php container,
      • db – for database,
      • cache or fpc – for cache containers;
  • the command can be anything that applies to that container, like “bash” or “bash composer”, etc.

I know the commands seem like “one more thing to learn”, but most of the time you will only use the first 4 commands.

How does the magic work?

Well, to see what the above commands translate to, just check the “dkc_short” file.

There are only 2 other interesting repositories:

The repositories are pretty small and not very hard to understand.

If you need to modify anything, just feel free to fork the repositories.

The conclusion

That’s about all you need to know about it, I’ve been using this setup for almost 2 years.

For me, it’s working as a charm and I was able to use Magento2 on ultraportable laptop with 8Gb RAM without any issues.

The (happy) end!

Written by Claudiu Persoiu

29 November 2018 at 3:00 PM

Passing Magento Developer Plus certification

with 3 comments

Over an year ago, I started working on the Magento platform. In last year’s spring, a colleague from Optaros took the Magento Developer Plus certification exam. Since then, I began to like the idea of taking the certification exam, more as a motivation to learn the ins and outs of Magento.

Few months ago I was enrolled into a company study group for the certification. This was the first time I was sponsored for a certification (yes, until now everything was with my money). Preparing in a study group was a whole different experience.

Those who have more experience in a field balance the situation for the others and can give better examples from their own experience. It’s easier to understand from concrete examples then to try to imagine the scenarios yourself.

The certification is available through Prometric. So when you decide that you’re ready you can go to the website to purchase the voucher and schedule the exam.

The price for a voucher is 260$, not exactly cheap, but if you get to convince your boss to pay, it probably won’t be so bad. 🙂

But let’s get to the more interesting subject, the preparation.

Materials
Magento is not doing very good on this subject, there are very few materials and they are not centralized.

My sources were:
Magento® Certified Developer Plus Exam Study Guide – it is compulsive to read the guide and try to find answers to all the questions in it;
Magento training – especially Fundamentals of Magento Development
– blogs – I don’t want to give any names, there are a lot of people that write about the problems that they encounter and blog about the exam.

Unfortunately there isn’t a way like for PHP, ZF and Symfony where you can find all you need in one place, basically it depends on your luck and searching skills, there isn’t an “official version”. Things become weird when you find different approaches that are version specific.

How did I prepare
I began with the video training. It’s not perfect by it’s very helpful. I think the problem with most certifications is that you don’t get to work with all the available modules, just like in PHP you don’t get to work that much with sockets and streams.

Even though you don’t get the code and sometimes it is hard to follow and transcribe the examples, I think that the video tutorials are one of the most important sources at the moment.

Secondly, with the Study Guide in my hand, I began to try to answer the questions from it. When I joined the Study Group, the work divided between all the members in the group. My advantage was that it was the second generation of the group and we could profit from the documentation already developed by the first group.

If you’re preparing by yourself, I think the most important thing is to start, that’s the hardest part. And if you don’t know where to start, Google search the Magento questions, there are already a lot of people that are posting the explanations.

Answers for the questions from the first chapters are the easiest to find. As the number of the chapter is getting bigger, the number of Google results decreases.

But after the first questions, you should understand what is all about and in theory you will no longer need the documentation.

Use Mage::log(Varien_Debug::backtrace(true, false)); for stack trace and xdebug (http://xdebug.org/) to see what’s going on behind the scene. With patience, all the questions find their answers.

Because it was a group, the study was easier for me, but even so, to be sure of the explanation I has to dive deep in the code.

The exam
Some of the questions are difficult, but there are also accessible ones. The questions in the exam are off all levels of difficulty.

For Plus, the exam takes 2h not 2.5h as it is specified in the guide.

If you opted for Plus, there are 11 questions from Enterprise and advanced difficulty questions, of which 7 correct ones are required to pass. Basically this is the difficulty difference. For this questions it matters how much Enterprise experience you have.

In the guide for each exam, the questions are broken in percentages for each chapter.

Because in the non Plus certification there are no Enterprise questions, you only have to answer the necessary percentage from the full exam in order to pass and it’s not required to have a certain percentage from a certain chapter.

Things that are done regularly are analyzed in detail, it is important to understand how each function that is approached in each chapter works and what is the purpose of all those tags in the xml files.

Usually there are things you work with, or at least with which there is a good probability you have been working from the modules listed in the guide.

Post exam
Before you leave the room you’ll know if you’ve passed or not. When you exit the room you’ll receive a printed paper with the correct number of questions from each section from the total number .

In case you haven’t been successful you’ll receive by mail from Magento a voucher with a discount for a future attempt. They state that you should study at least 3 more weeks before you try again. Anyway, after you’ve taken the exam you’ll have a better view over your overall knowledge for a future attempt.

After few days (3 in my case) you will be able to see your profile on the Magento website as a reference.

The diploma got to Romania in about a month, the delivery address is the one from the Magento website account.

Best of luck!

Written by Claudiu Persoiu

16 February 2013 at 8:23 PM

Posted in Magento,PHP

Tagged with , , ,

Overwriting and deactivating Observers in Magento

with 2 comments

Sometimes we need to overwrite an observer. The first way that usually comes in mind is overwriting the model. Usually is named Observer.php, because this is the “best practice”.

And NO, you don’t have to overwrite the model. Observer.php doesn’t extend anything anyway and usually contains all of the module’s observers, so you can’t overwrite the same observer in more than one module.

How it works?
In magento when a new observer is added, it must have an unique identifier. This identifier is the key!

Actually, there is another element: “area”. When Mage::dispatchEvent(…) is performed, events will be dispatched using “area” and “identifier”.

For example, the admin notification system, which is observing “controller_action_predispatch”, will run:

=> "global"(area)
=> "controller_action_predispatch"(event)
=> "adminnotification"(identifier)

then:

=> "adminhtml"(area)
=> "controller_action_predispatch"(event)
=> "adminnotification"(identifier)

If the event was in the frontend area, it would be: “global” then “frontend”.

Overwriting
Overwriting is in fact a observer defined in the same config area as the original event (global, frontend or adminhtml), attached to the same event and with the same identifier as the original observer (e.g. adminnotification).

Let’s say we have to overwrite “adminnotification”. This observer is in Mage/AdminNotification. The unique identifier is defined in etc/config.xml:

...
  <adminhtml>
...
    <events>
      <controller_action_predispatch>
        <observers>
          <adminnotification>
            <class>adminnotification/observer</class>
            <method>preDispatch</method>
          </adminnotification>
        </observers>
      </controller_action_predispatch>
    </events>
...
  </adminhtml>
...

From the example above we can see:
– area: adminhtml
– event: controller_action_predispatch
– identifier: adminnotification

The module activation file will be: app/etc/modules/CP_AdminNotification.xml

<?xml version="1.0"?>
<config>
  <modules>
    <CP_AdminNotification>
      <active>true</active>
      <codePool>local</codePool>
      <depends>
        <Mage_AdminNotification/>
      </depends>
    </CP_AdminNotification>
  </modules>
</config>

I’ve added dependencies because without the original module, this module will be useless.

There’s a “best practice” to name a module that is overwritten with the same name as the original module.

The configuration file for this module, will contain practically everything you’ll need for the overwriting: area, event and identifier. The file is located in app/code/local/CP/AdminNotification/etc/config.xml:

<?xml version="1.0"?>
<config>
  <modules>
    <CP_AdminNotification>
      <version>0.0.1</version>
    </CP_AdminNotification>
  </modules>
  <global>
    <models>
      <cp_adminnotification>
        <class>CP_AdminNotification_Model</class>
      </cp_adminnotification>
    </models>
  </global>
  <adminhtml>
    <events>
      <controller_action_predispatch>
        <observers>
          <adminnotification>
            <class>cp_adminnotification/observer</class>
            <method>overwrittenPreDispatch</method>
          </adminnotification>
        </observers>
      </controller_action_predispatch>
    </events>
  </adminhtml>
</config>

The observer should contain all the new logic. The file is in app/code/local/CP/AdminNotification/Model/Observer.php, just like you would probably expect from the structure above.

<?php

class CP_AdminNotification_Model_Observer {

  public function overwrittenPreDispatch(Varien_Event_Observer $observer) {
    // noua logica din observer
  }
}

Disabling
Disabling is preaty similar to overwriting, the difference is in the config and the fact that an observer file is not needed anymore, because there isn’t a new logic.

The new config.xml file is:

<?xml version="1.0"?>
<config>
...
  <adminhtml>
    <events>
      <controller_action_predispatch>
        <observers>
          <adminnotification>
            <type>disabled</type>
          </adminnotification>
        </observers>
      </controller_action_predispatch>
    </events>
  </adminhtml>
</config>

Written by Claudiu Persoiu

17 May 2012 at 9:59 PM

Posted in Magento,PHP

Tagged with ,

Magento – Create a custom shopping cart price rule

with 2 comments

This is not a tutorial about setting up a Shopping Cart Price Rule in Magento, but rather about implementing a new one.

A new type of rule in Magento needs a couple of things:
– modify the admin area to add the new rule using an observer for adminhtml_block_salesrule_actions_prepareform,
– a way to apply the new rule using an observer for salesrule_validator_process.

Let’s build an example. Let’s say there is a Shopping Cart Price Rule that offers different discounts according to the number of products in the cart. The value that’s going to be used for the discount increment ($step) will be calculated. The first product will not receive a discount, the second product will receive a discount of $step, the third product will have a discount of 2*$step, until the maximum discount value will be reached. The following products will have a maximum discount. Ex:
Discount Amount = 50
Discount Qty = 5
Step = Discount Amount / Discount Qty = 10

Discount outcome:
– 0% prod 1
– 10% prod 2

– 50% prod 6
– 50% prod 7

The first step is the module activation using the file: app/etc/modules/CP_ProductNrDiscount.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <CP_ProductNrDiscount>
            <active>true</active>
            <codePool>local</codePool>
        </CP_ProductNrDiscount>
    </modules>
</config>

The first observer, adminhtml_block_salesrule_actions_prepareform, must be in the “adminhtml” section of the config, because it will involve the admin. This observer will have access to the admin form, in order to modify it.

The second observer, salesrule_validator_process, can be in the “frontend” or “global” section of the config. If it’s in the frontend section, it will only apply to the frontend section. If it’s in the global section it will also apply to backend. Usually, global is necessary when there are actions on the cart in the backend.

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <CP_ProductNrDiscount>
            <version>0.0.1</version>
        </CP_ProductNrDiscount>
    </modules>
    <global>
        <models>
            <productnrdiscount>
                <class>CP_ProductNrDiscount_Model</class>
            </productnrdiscount>
        </models>
        <events>
            <salesrule_validator_process>
                <observers>
                    <productnrdiscount>
                        <type>model</type>
                        <class>productnrdiscount/observer</class>
                        <method>salesruleValidatorProcess</method>
                    </productnrdiscount>
                </observers>
            </salesrule_validator_process>
        </events>
    </global>
    <adminhtml>
        <events>
            <adminhtml_block_salesrule_actions_prepareform>
            <observers>
                <productnrdiscount>
                    <type>model</type>
                    <class>productnrdiscount/observer</class>
                <method>adminhtmlBlockSalesruleActionsPrepareform</method>
                </productnrdiscount>
            </observers>
            </adminhtml_block_salesrule_actions_prepareform>
        </events>
    </adminhtml>
</config>

As you can see above, there must be an Observer model that will have the two methods which modify the admin and apply the discount.

<?php
/**
 * Number of product discount module
 *
 * @author Claudiu Persoiu http://blog.claudiupersoiu.ro
 */
class CP_ProductNrDiscount_Model_Observer {

    // The new rule type
    const PRODUCT_NR_DISCOUNT = 'product_nr_discount';

    /**
     * Add the new rule type to the admin menu
     *
     * @param Varien_Event_Observer $observer
     */
    public function adminhtmlBlockSalesruleActionsPrepareform
              (Varien_Event_Observer $observer) {
        // Extract the form field
        $field = $observer->getForm()->getElement('simple_action');
        // Extract the field values
        $options = $field->getValues();
        // Add the new value
        $options[] = array(
            'value' => self::PRODUCT_NR_DISCOUNT,
            'label' => 'Product Number Discount'
        );
        // Set the field
        $field->setValues($options);
    }

    /**
     * Apply the discount
     * The discount will be applied for at least 2 products increasing
     * with a "step" for each product, where "step" is
     * maximum discount / number of products.
     *
     * @param Varien_Event_Observer $observer
     */
    public function salesruleValidatorProcess(Varien_Event_Observer $observer) {

        // $item typeof Mage_Sales_Model_Quote_Item
        $item = $observer->getEvent()->getItem();
        // $rule typeof Mage_SalesRule_Model_Rule
        $rule = $observer->getEvent()->getRule();

        // Number of products
        $qty = $item->getQty();

        // We must check the rule type in order to isolate our rule type
        if($rule->getSimpleAction() == self::PRODUCT_NR_DISCOUNT && $qty > 1) {

            // Extract rule details
            $discountAmount = $rule->getDiscountAmount();
            $discountQty = $rule->getDiscountQty();

            // Discount step
            $step = $discountAmount/$discountQty;

            // Discount calculation
            $discount = 0;
            for($i = 1; $i < $qty; $i++) {
                $itemDiscount = $i * $step;
                // If the discount is bigger then the maximum discount
                // then the maximum discount is used
                if($itemDiscount > $discountAmount) {
                    $itemDiscount = $discountAmount;
                }

                $discount += $itemDiscount;
            }
            // Effective discount
            $totalDiscountAmount = ($item->getPrice() * $discount)/100;

            // Discount in percent for each item
            $item->setDiscountPercent($discount / $qty);

            // Setting up the effective discount, basically this is the discount value
            $result = $observer->getResult();
            $result->setDiscountAmount($totalDiscountAmount);
            $result->setBaseDiscountAmount($totalDiscountAmount);

        }
    }

}

This observer will run at each request if there are items in cart that for which the rule is applicable. If the discount should be applied only for specific products, there can be filtered using the rule’s “Conditions” tab, just as you would normally do.

Written by Claudiu Persoiu

8 March 2012 at 9:37 PM

Magento native stack trace

with 5 comments

There are moments when you need to see the stack trace, to know how a certain point was reached. There are two native functions for that in PHP: debug_backtrace() si debug_print_backtrace. The first one returns an array and the second will print the stack trace to the screen.

The problem is that this functions must be customized for Magento, because it is very possible that when you’re running debug_backtrace()  you can run out of memory before you can send the output to a log file.

Magento has a native function for that purpose: Varien_Debug::backtrace([bool $return = false], [bool $html = true], [bool $withArgs = true]). In order to send the resulting stacktrace to a log file you simply all it with:

Mage::log(Varien_Debug::backtrace(true, false));

This technique is very useful when you need to see where an certain object is initialized, and what methods were executed up to that point.

Written by Claudiu Persoiu

25 February 2012 at 11:48 AM

Posted in Magento,PHP

Tagged with , , ,

Magento dead end – Breadcrumbs

without comments

In one of my adventures in the Magento code. I’ve encountered the following problem: I had to add a link to the breadcrumb.

As the documentation is not so great, after a little debugging (not a lot), I’ve got in to the core Mage_Page_Block_Html_Breadcrumbs.

The method is quite self-explanatory: addCrumb($crumbName, $crumbInfo, $after = false). Since I was there, I took a look inside:

function addCrumb($crumbName, $crumbInfo, $after = false)
{
  $this->_prepareArray($crumbInfo, array('label', 'title', 'link', 'first', 'last', 'readonly'));
  if ((!isset($this->_crumbs[$crumbName])) || (!$this->_crumbs[$crumbName]['readonly'])) {
    $this->_crumbs[$crumbName] = $crumbInfo;
  }
  return $this;
}

What’s interesting is the $after parameter, as you can see, even though it has a default value, is not used anywhere. The rest work’s as expected, probably this is why people don’t complain so much about it.

Written by Claudiu Persoiu

3 February 2012 at 10:03 PM

Posted in Magento,PHP

Tagged with ,