Magento is the world's leading e-commerce platform. The problem is, it is horribly coded and extremely difficult to work with. Don't believe me? I can prove it. #m2h8r
Don't wanna be here? Send us removal request.
Text
Payment-Capture circularity
If you try to capture a payment without an invoice, it creates an invoice, then tells the invoice to capture the payment, then the invoice calls capture with itself as the parameter, putting you back in that same method you were in before.
I can’t see any way that could go wrong. (Spoiler alert...)
CaptureOperation line 29, in case you like reading.
0 notes
Text
Magento and Dates
Magento transmits its date-times between the database and PHP using strings. Yes, that’s right; string representations of the dates, not “string representations of a number”. Those strings also don’t include the timezone, in case you were wondering.
0 notes
Text
Interviewing Magento Developers
I have to interview numerous Magento developers. A question I love to ask is, “Do you enjoy working with Magento?”
What follows is often a very long awkward pause...
The question is at least helpful in determining how candidates can come up with euphemisms when put on the spot. “Euphemisms” is the nice way of putting it, naturally.
You can always follow up with questions such as “What are some strengths/weaknesses of the Magento platform?” if you desire delving deeper into their understanding.
To save you some suspense, the candidates who like working with Magento the most are those who have never worked with any other platform before.
Generally speaking: Would you hire a developer who works with a terrible platform and sees nothing wrong with it? => That indicates a lack of experience and competence. Conversely, would you hire a developer who hates the platform they work with? => That developer cannot be happy at their job.
0 notes
Photo
Very often when you work with Magento you’ll do a web search and find that others have experienced the exact same bug as you. Often, years ago! You’ll also find the bug is CLOSED as “cannot reproduce”, meaning the bug is still a problem today.
This happens so often that it frustrates developers to the point where they even proactively ask that the bug not be closed as “cannot reproduce”. Ha!
Also; that bug? It’s still happening! From 2.1.1 to the latest code. Sometimes customers try to check out and they get the friendly error message “No such entity with cartId.” Oh, and that bug is CLOSED, too.
What’s causing it? When an admin updates a product in the admin section, Magento helpfully wipes out all the quotes (shopping carts) so that people don’t accidentally check out with products that have suddenly become invalid. The problem is that customers on the checkout page get an unfriendly error when they try to pay, with no way to fix it themselves.
Also, this can happen when people check out with a really, really old cart. There's a cron-job that deletes old carts (i.e. quotes) that have been hanging around too long. But sometimes it does that when people are in the middle of checking out.
The true crime here is that the Magento people don’t really understand, acknowledge [in their github bug tracking], or have fixed these problems [yet].
0 notes
Photo
Wow, nice bug!! Though I don’t know why it’s “Closed”. UI Components are still as painful as ever.
I especially love the expected and actual results. Hilarious!
0 notes
Text
Hiding exceptions
Thanks so much, Magento.
try { $res = $this->_getUploader()->move($fileName, $renameFileOff); return $res['file']; } catch (\Exception $e) { return ''; }
0 notes
Text
magento.dual table
Let’s say you want to execute a simple statement like:
select @@global.event_scheduler;
That select works fine in MySQL, but doesn’t have a “table”. In the Magento code, you can’t select with out a “from”.
If you leave the table empty, it crashes.
If you put “dual” as the table, because selecting from dual works in MySQL also, it says, “Base table or view not found: 1146 Table 'magento.dual' doesn't exist”. Whee!
So, you might as well just put ANY table you can think of there, because selecting the @@global variable from any table will return its value.
$select = $connection->select()->from('admin_user', '@@global.event_scheduler');
0 notes
Text
Committing does not commit
In possibly the worst transaction management code I’ve ever seen, Magento has code that when you call “commit” on a transaction, it does not commit.
Magento has a concept of “transaction levels” for nested transactions. The problem is, when you call commit() on a nested-transaction, it doesn’t necessarily commit at all! So whatever data you think you wrote to the database in a sub-transaction – sorry, it’s not there.
Similarly, when you roll-back a sub-transaction, you end up rolling back the parent transaction as well.
Don’t believe me? Check out this code from vendor\magento\framework\DB\Adapter\Pdo\Mysql.php lines 243-284:
* Begin new DB transaction for connection * * @return $this * @throws \Exception */ public function beginTransaction() { if ($this->_isRolledBack) { throw new \Exception(AdapterInterface::ERROR_ROLLBACK_INCOMPLETE_MESSAGE); } if ($this->_transactionLevel === 0) { $this->logger->startTimer(); parent::beginTransaction(); $this->logger->logStats(LoggerInterface::TYPE_TRANSACTION, 'BEGIN'); } ++$this->_transactionLevel; return $this; } /** * Commit DB transaction * * @return $this * @throws \Exception */ public function commit() { if ($this->_transactionLevel === 1 && !$this->_isRolledBack) { $this->logger->startTimer(); parent::commit(); $this->logger->logStats(LoggerInterface::TYPE_TRANSACTION, 'COMMIT'); } elseif ($this->_transactionLevel === 0) { throw new \Exception(AdapterInterface::ERROR_ASYMMETRIC_COMMIT_MESSAGE); } elseif ($this->_isRolledBack) { throw new \Exception(AdapterInterface::ERROR_ROLLBACK_INCOMPLETE_MESSAGE); } --$this->_transactionLevel; return $this; }
So let’s say you’re doing, I dunno, e-commerce programming and you have to process a payment on a 3rd-party system and have your system’s result recorded no matter what exceptions happen in that 3rd-party system, that’s the kind of time you’d start a transaction within your transaction. But if there’s any error in that 3rd-party system; sorry, your entire transaction is GONE.
This is some mickey-mouse-level coding bullshit. Magento should have its status as “an enterprise-level e-commerce system” completely revoked with this kind of code in their transaction management.
(And for those wondering, yes, nested transactions are supported in MySQL through the concept of “save points”.)
0 notes
Text
“Cart” is deprecated... but why?
Something interesting found in the Magento code, the entire “Cart” object is deprecated. But there’s no explanation as to why. So as a developer, seeing this object still being used, you may wonder “why?” as you try to use it and you get warnings... have fun!
https://github.com/magento/magento2/commit/c4e9a77403655646a18d4277de96429c86bd34fa#diff-aad85c7d6b9a0cf0c2284b3db8efe796
(The user’s cart, btw, is represented by a “quote” entity, for those that are new to Magento.)
0 notes
Text
Cannot save a customer that exists
When running this simple code to save an existing customer:
$customer = $this->customerRepository->get($row['email'], $this->storeManager->getWebsite()->getId()); $customer->setCustomAttribute('custom_attr', 'value'); $this->customerRepository->save($customer);
I was presented with the error:
Error: A customer with the same email already exists in an associated website.
And just to be clear; that customer was in the website specified by that ID. It was the correct ID.
I had to double-check to make sure I was using the right method to update a customer. Looking at the Customer API, it states:
/** * Create or update a customer. * * @param \Magento\Customer\Api\Data\CustomerInterface $customer * @param string $passwordHash * @return \Magento\Customer\Api\Data\CustomerInterface * @throws \Magento\Framework\Exception\InputException If bad input is provided * @throws \Magento\Framework\Exception\State\InputMismatchException If the provided email is already used * @throws \Magento\Framework\Exception\LocalizedException */ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $passwordHash = null);
Why did I get that error message? Well, it turns out you can’t update a “system attribute” (check the “is_system” for the attribute in “customer_eav_attribute”), and this attribute had been created incorrectly as a system attribute.
Maybe I’m crazy, but I would have saved a some debugging time if the error message was, “You cannot update a system attribute using the Customer API. You are attempting to do that inserting a customer.”
0 notes
Text
Magento’s Encryption...
I didn’t write this, but check this out: http://www.openwall.com/lists/oss-security/2016/07/19/3
Yes, the encrypted-values contain the method used for encryption, therefore they can be forged and with some effort are even decryptable. This is no joke, people.
And what the article doesn’t mention is, without some sort of migration tool, Magento cannot even change this, because many vendors using the software will already have tons of data using the existing method. (It could, I suppose, be split on the “:” into different columns, but those columns would still be just as writable as the hash-value column for those with database access.)
Oh, also: this was reported a year ago, but Magento still works the same way.
0 notes
Text
Caches disabled after module install
Sometimes in Magento 2 when you enable a module and then run setup:upgrade your Magento installation will grind to a halt on every request. Why? Turns out Magento decided to disable every cache for you without warning or explanation, and with nothing in the log file. Just for fun.
Magento disables the caches when installing a module so that they don’t interfere with the module installation, and then it tries to re-enable them afterwards. If an error occurred anywhere during the module installation process, the caches won’t be re-enabled; you’ll be left with a broken Magento. This is somewhat understandable, but it would be nice if it warned you!
Other times, the module installation will work but your caches will still be disabled! Magento sometimes experiences an error when re-enabling its own caches, and may tell you “Could not complete setup:cache:enable”. This has been known and acknowledged for about 9 months -- but yes, it still happens.
So if you install a module and you find your system running slower than normal, check the cache:status, and you may be surprised to find:
0 notes
Text
Get/set area-code fun!
One of my favorite pieces of code that I’ve come across so far are the getter and setter methods for the area-code. Take a look:
/** * Set area code * * @param string $code * @return void * @throws \Magento\Framework\Exception\LocalizedException */ public function setAreaCode($code) { if (isset($this->_areaCode)) { throw new \Magento\Framework\Exception\LocalizedException( new \Magento\Framework\Phrase('Area code is already set') ); } $this->_configScope->setCurrentScope($code); $this->_areaCode = $code; } /** * Get area code * * @return string * @throws \Magento\Framework\Exception\LocalizedException */ public function getAreaCode() { if (!isset($this->_areaCode)) { throw new \Magento\Framework\Exception\LocalizedException( new \Magento\Framework\Phrase('Area code is not set') ); } return $this->_areaCode; }
Due to the way this is coded:
You cannot set the area code if it’s already set – Exception!
You cannot get the area code if it’s not already set – Exception!
You cannot check if the area code is set before trying to set it – Exception!
And yet Magento2 will happily throw an exception and choke if you try to run code with no area code set or the wrong area code set.
The proper way to run code in a different area-code is to emulate that area code using emulateAreaCode(). Totally intuitive, right?
Alan Storm’s blog suggests to set the area code in the constructor, however if you do that and you run a command, the command’s execute method will call setAreaCode() – Exception! So the proper place for you to set the area code (for a custom command) is in the execute method. Set it once, and only once, or be punished.
Other people suggest to run code like this:
if (!$this->state->getAreaCode()) { $this->state->setAreaCode('adminhtml'); }
This code would be perfectly normal and intuitive to write. Unfortunately it is guaranteed to throw an exception; it will either throw an exception because the area code is not set and you're trying to get it, or throw an exception if it is set because you're trying to set it.
0 notes
Text
Critical errors on vanilla install finally fixed...?
Once you install Magento 2.1 and start to run it, your logs will fill up with a bunch of main.CRITICAL: Broken reference errors.
This seems pretty silly for a vanilla install that you haven't had a chance to break yet. But never you fear; it comes broken out-of-the-box.
Furthermore, this renders the error logs somewhat useless because they're filled up with a bunch of crap. They're like the logs-that-cried-wolf (while missing the actual wolf most of the time).
It seems they've finally fixed that bug! The fix? -> Change the log level of the critical error messages to info.
0 notes
Text
Why hate?
Let’s face it, griping online about coding issues is juvenile, and not a productive use of time. So you might be wondering why this blog exists. Why troll Magento? Why not just log bugs with Magento? Or better yet, why not provide pull-requests that help to solve the problems? Well, I do that. But there’s some other issues that simple bug-logs will never solve:
1. Magento is advertised and promoted as being a high-quality, well-organized, highly-customizable and easily-extensible system. The problem here is that it is undeniably not that, in any way at all. That part of it is a lie, a trick that will cost you time and money.
2. From the outside, developers who are evaluating Magento have no idea what they’re getting into. There’s essentially zero warning. Hopefully, this blog will at least shed some light onto what they’ll find crawling around in the mud when they flip over that rock.
3. Magento is advertised as being able to be extended without touching the core. Sorry, that’s not true. Arguably, it’s a lie; because the people saying it must know better. Again there’s no warning to developers. So this blog will only (mostly?) discuss issues that expose that fallacy.
4. Many of Magento’s issues are multi-layered complexities, and/or they’re actual architectural issues. Sometimes the entire approach is wrong, from an e-commerce standpoint. Those you can’t just log a bug to remedy. And even if you did, and supplied a breaking change pull-request to fix them, the fact remains: Developers will have to deal with the issues today and for a while yet to come.
5. Many of Magento’s errors are misleading, sometimes hilariously so. Maybe if an error is mentioned here and people are experiencing it, they’ll find this blog and it’ll explain to them what’s really going on.
Yeah, so let’s hate. Feel free to hate back. Isn’t that what the internet is for? (*ahem* cat videos...)
0 notes
Text
Address phone number is REQUIRED
For some reason, the phone number is a required field for addresses. It makes sense, right? I mean, whenever anyone asks you “where do you live?”, without also giving them your phone number they’d naturally have no idea where to go. Ugh...
The logic behind this requirement is that some shipping providers require the address. Among those listed is FedEx and USPS; well, I’ve delivered shipment data to them (through fulfillment companies) and I can tell you they do not require the phone number.
The phone number is an EAV attribute of the customer_address model, and as such it has an “is_required” boolean, which is set to true by default. The problem is, setting it to false doesn’t do everything you need, because there are places in the code where this isn’t even checked -- that’s right, the validation of the phone number is hard-coded in the code. (*cough*AddressRepository*cough*Order-Address-Validator*cough*AbstractAddress*cough*)
That’s right: Magento, which prides itself on being configurable, doesn’t let you specify which customer fields are required or not. Decidedly inflexible indeed.
Thankfully some kind soul on the internet provided a module that will make the phone number not required (yeah, it’s 17 directories and 15 files).
How does it work? After the validation takes place, the module removes any validation failures for the phone number (end-of-pipe), meaning all the validation has to run and then essentially “un-run” after it’s run. Furthermore, the module compares the validation messages in English to determine if there’s a failure.
Also, the module copies the entire function AddressRepository->save() into a subclass just to call a private function AddressRepository->_validate(), so if there’s ever any changes to save(), that will break.
I don’t blame the module code for this, because actually it’s the only way to accomplish this task. Why un-run the validation by comparing error-messages? Because we still want the rest of the normal validation to run, no matter what it may be. Why duplicate the entire save() function into a subclass? Because it calls a private _validate() function that we don’t have access to in a plugin. [There’s another way to do this, but it involves both a delegate function + a plugin, which is arguably messier although it means we wouldn’t need to copy the save() function.]
To be fair, they are fixing this for Magento 2.2. Also, there was a lengthy discussion about this topic here.
So why bitch about it?
It’s yet another one of those “gotchas” when working with Magento 2.
It is something that should be very simple, yet it is made extremely difficult, and the [current] workaround is messy and fragile.
The issue was reported last August; it's been 9 months and the code is not yet fixed.
Should I mention it's not documented? #CaptainObvious
Let’s be real, folks. A properly-written e-commerce system should simply not have this kindergarten-level problem; it’s costing many developers lots of time to figure out and solve.
0 notes