Post by jjIridium » Tue Jan 21, 2025 3:27 pm

Long-time developer, first time using Opencart though. I'll try to be brief.

Opencart version: 4.0.2.0
PHP version: 8.2
Extension logic: I'm working on an extension that calls to my client's inventory API after an order is placed.
Problem: I can install my extension, but I can't trigger the event.
Documentation used: https://github.com/opencart/opencart/wiki/Events-System, sample OC extensions, misleading chatGPT results.

Glad to report that I was able to create/install an extension easily enough. However, when I check out in my store, I do not see the logs I am printing from the top of my extension. I suspect an issue with namespacing, as I've been having trouble parsing the documentation / example code.

Speaking of code, here is (an abbreviated version of) mine. First, the file structure:

Code: Select all

/public_html
   /extension
       /iocart_update_inventory
          /admin
             /controller/module
                iocart_update_inventory.php
             /language/en-gb/module
                iocart_update_inventory.php
             /view/template/module
                iocart_update_inventory.twig
          /catalog
             /controller
                 /iocart_update_inventory
                    update_inventory.php
... It was a little unclear to me in the documentation if we are supposed to use the word "module" or the name of our module... Also, the requirements for directory structure as it relates to pathing is not clear to me.

Okay, on to some more code. Here's an abbreviated version of my admin controller:

Code: Select all

<?php
namespace Opencart\Admin\Controller\Extension\IocartUpdateInventory\Module;

class iocartupdateinventory extends \Opencart\System\Engine\Controller {
    public function index(): void {
      // lots of boring language stuff here
      ...
      $this->response->setOutput($this->load->view('extension/iocart_update_inventory/module/iocartupdateinventory', $data));
    }

    public function save(): void {
        // eh, same here because my extension doesn't really have any settings yet.
        ...
        $this->response->setOutput(json_encode($json));
    }

    public function install(): void {
        $this->load->model('setting/event');
        // Here's the good part. Notice I'm using the dot notation instead of the pipe because OC version 4.0.2.0. 
        // but not sure if I got the action path correct. I've tried all kinds of variations of casing, directory structure, and underscores/no underscores in the extension name.
        $this->model_setting_event->addEvent([
            'code'        => 'iocart_update_inventory',
            'description' => 'Update inventory in IOCart when an order is placed',
            'trigger'     => 'catalog/model/checkout/order/addOrder/after',
            'action'      => 'extension/iocartupdateinventory/iocartupdateinventory.updateInventory'
        ]);
    }

    public function uninstall(): void {
        $this->load->model('setting/event');
        $this->model_setting_event->deleteEventByCode('iocart_update_inventory');
    }
}
By now I'm sure you're noticing a lot of places where I wasn't sure if I should snake-case my extension name or not. I'm making everything in this snippet snake-cased for your sake, but I've tried many iterations with different strategies at this point...

Okay last one I promise (because there's no need to include the language or twig files). Here's the catalog controller where the magic should happen:

Code: Select all

<?php
namespace Opencart\Catalog\Controller\Extension\iocartupdateinventory\updateinventory;

class iocartupdateinventory extends \Opencart\System\Engine\Controller {
    private function getAccessToken() {
        // you'll just have to trust me that there's some access token logic here... removed for brevity.
        return $token_data['access_token'];
    }

    public function updateInventory(&$route, &$args, &$output) {
        $this->log->write('TEST TEST WHY U NO LOG NOTHING EVER');
	
        try {
            // Get OAuth token
            $access_token = $this->getAccessToken();
	    ...
            // $this->log->write('Successfully updated inventory for order ');
        } catch (\Exception $e) {
            // Log error
            $this->log->write('Failed to update inventory: ' . $e->getMessage());
        }
    }
}
Again, super truncated for your sake but it's even clear to me just looking at the code now that I am completely lost when it comes to matching up extension naming, file pathing, and method naming to get PHP/OC to automagically glue everything together.

Please advise on how I should proceed with updating my module naming and file structure. Thanks! - JJ

Newbie

Posts

Joined
Sun Jan 12, 2025 1:04 am

Post by grgr » Wed Jan 22, 2025 8:42 am

This:

Code: Select all

<?php
namespace Opencart\Catalog\Controller\Extension\iocartupdateinventory\updateinventory;

class iocartupdateinventory extends \Opencart\System\Engine\Controller {
does not match with this:

Code: Select all

          /catalog
             /controller
                 /iocart_update_inventory
                    update_inventory.php

-
Image Image Image
VIEW ALL EXTENSIONS * EXTENSION SUPPORT * WEBSITE * CUSTOM REQUESTS


User avatar
Active Member

Posts

Joined
Mon Mar 28, 2011 4:08 pm
Location - UK

Post by jjIridium » Wed Jan 22, 2025 1:10 pm

grgr wrote:
Wed Jan 22, 2025 8:42 am
This:

Code: Select all

<?php
namespace Opencart\Catalog\Controller\Extension\iocartupdateinventory\updateinventory;

class iocartupdateinventory extends \Opencart\System\Engine\Controller {
does not match with this:

Code: Select all

          /catalog
             /controller
                 /iocart_update_inventory
                    update_inventory.php
... Okay, so I would need to update my namespace to:

Code: Select all

<?php
namespace Opencart\Catalog\Controller\iocart_update_inventory\update_inventory;
class iocartupdateinventory extends \Opencart\System\Engine\Controller
?

I understand that the namespaces need to match the file structure, but do I keep the snake_casing? Should I stop snake_casing my filenames? Do I switch to PascalCase? Going to try it now, will report back.

Newbie

Posts

Joined
Sun Jan 12, 2025 1:04 am

Post by softmonke » Wed Jan 22, 2025 4:17 pm

Eh, there's no need to use snake case. I generally follow the OpenCart default modules/extensions, so I use pascal case for my class name and namespace.

Just stick to pascal case since that seems to be used across the OpenCart codebase.

Anyway, it seems like your file paths and namespace/class name are all over the place, which is why your event isn't triggering.

If your event's action function is named "updateInventory" and is located in the file "update_inventory.php" directory "extension/iocart_update_inventory/catalog/controller/iocart_update_inventory", then your event action should be:

Code: Select all

extension/iocart_update_inventory/iocart_update_inventory/update_inventory.updateInventory

Your "trigger" parameter for addEvent seems to be correct, but I think you also missed out on "status" and "sort_order". Notice that the difference here is the "update_inventory" line, which is referring to your "update_inventory.php" file containing your updateInventory trigger function.


Then, your controller namespace and class should be:

Code: Select all

namespace Opencart\Catalog\Controller\Extension\IocartUpdateInventory\IocartUpdateInventory;

class UpdateInventory extends \Opencart\System\Engine\Controller {
  public function updateInventory(): void {
    // This should be triggered when addOrder is called
    $this->log->write('test test test');
  }
}
Here, notice that both your class name and namespace are wrong - to prevent confusing yourself, just know that you should set the class name as the pascal case of the name of the file (i.e. update_inventory.php should have the class name UpdateInventory). Namespace on the other hand, should be the file path to your class.

Check out our ever-growing list of extensions for OpenCart here.
Some useful extensions for a better admin experience: Image File Manager ProDrag & Drop Sort Order

Reach out to us at hello@softmonke.com for your OpenCart web development needs or feedback for our extensions.


User avatar
Active Member

Posts

Joined
Tue May 23, 2023 4:42 am


Post by jjIridium » Sat Jan 25, 2025 12:02 pm

Thank you both @grgr + @softmonke for your help! I've made some changes and still struggling here. I hope that the changes I've made (and related test cases) will help me walk it in to the goal line.

The updated file structure:
Image

For simplicity & dummy-proofing, there is no more snake case anywhere. All the file names are one-word.

And then the updated admin controller (`public_html/extension/iocartupdateinventory/admin/controller/module/iocartupdateinventory.php`):

Code: Select all

<?php
namespace Opencart\Admin\Controller\Extension\IocartUpdateInventory\Module;

class iocartupdateinventory extends \Opencart\System\Engine\Controller {
    public function index(): void {
      // lots of boring language stuff here
      ...
      $this->response->setOutput($this->load->view('extension/iocartupdateinventory/module/iocartupdateinventory', $data));
    }

    public function save(): void {
        // eh, same here because my extension doesn't really have any settings yet.
        ...
        $this->response->setOutput(json_encode($json));
    }

    // Here I've begun testing four different action paths, and four different triggers
    // Also added status and sort_order to each.
    public function install(): void {
        $this->load->model('setting/event');
        
        // This first one is similar to the original
       $this->model_setting_event->addEvent([
            'code'        => 'iocartupdateinventory',
            'description' => 'Test trigger after an order is placed',
            'trigger'     => 'catalog/model/checkout/order/addOrder/after',
            'action'      => 'extension/iocartupdateinventory/iocartupdateinventory.updateInventory',
            'status'      => 1,
            'sort_order'  => 0
        ]);

        // base extension path + filename.function
        $this->model_setting_event->addEvent([
          'code'        => 'iocartupdateinventory2',
          'description' => 'Test trigger after an item is added to the cart',
          'trigger'     => 'catalog/controller/checkout/cart/add/after',
          'action'      => 'extension/iocartupdateinventory.updateInventory',
          'status'      => 1,
          'sort_order'  => 0
        ]);

        // Mimics the advice I got from @softmonke, but with the updated directory naming
        $this->model_setting_event->addEvent([
          'code'        => 'iocartupdateinventory3',
          'description' => 'Test triggered after the admin dashboard is loaded',
          'trigger'     => 'admin/controller/common/dashboard/after',
          'action'      => 'extension/iocartupdateinventory/iocartupdateinventory/iocartupdateinventory.updateInventory',
          'status'      => 1,
          'sort_order'  => 0
        ]);

        // here I'm even adding `catalog/controller`, which I noticed is not normally necessary in the docs. But... sanity check.
        $this->model_setting_event->addEvent([
          'code'        => 'iocartupdateinventory4',
          'description' => 'Test triggered when a user visits the home page',
          'trigger'     => 'catalog/controller/common/home/after',
          'action'      => 'extension/iocartupdateinventory/catalog/controller/iocartupdateinventory/iocartupdateinventory.updateInventory',
          'status'      => 1,
          'sort_order'  => 0
        ]);
    }

    public function uninstall(): void {
        $this->load->model('setting/event');
        $this->model_setting_event->deleteEventByCode('iocartupdateinventory');
        $this->model_setting_event->deleteEventByCode('iocartupdateinventory2');
        $this->model_setting_event->deleteEventByCode('iocartupdateinventory3');
        $this->model_setting_event->deleteEventByCode('iocartupdateinventory4');
    }
}
... And the updated catalog controller:

Code: Select all

<?php
// namespace assist from @softmonke
namespace Opencart\Catalog\Controller\Extension\IocartUpdateInventory\IocartUpdateInventory;

class IocartUpdateInventory extends \Opencart\System\Engine\Controller {
    public function updateInventory(&$route, &$args, &$output) {
        $this->log->write('Someday I will see this log in the output :(');
    }
}
I feel like... this is pretty close. Please review the new directory structure + namespacing and let me know if anything strange jumps out.

Attachments

???
Screenshot 2025-01-24 at 7.22.03 PM.png

Newbie

Posts

Joined
Sun Jan 12, 2025 1:04 am

Post by jjIridium » Sat Jan 25, 2025 1:16 pm

Another quick question, does the extension zip file I'm uploading need to have an `upload` directory above the `/admin` and `/catalog` directories?

Newbie

Posts

Joined
Sun Jan 12, 2025 1:04 am

Post by JNeuhoff » Sat Jan 25, 2025 5:44 pm

jjIridium wrote:
Sat Jan 25, 2025 1:16 pm
Another quick question, does the extension zip file I'm uploading need to have an `upload` directory above the `/admin` and `/catalog` directories?
No. But it needs the 'install.json' file.

Export/Import Tool * SpamBot Buster * Unused Images Manager * Instant Option Price Calculator * Number Option * Google Tag Manager * Survey Plus * OpenTwig


User avatar
Guru Member

Posts

Joined
Wed Dec 05, 2007 3:38 am


Post by jjIridium » Sun Jan 26, 2025 9:17 am

The reason I'm asking is because I noticed that on the Admin panel's Extensions > Extensions page, even after I've installed the extension (success message) it still says Disabled:

Image

I also noticed that in the public_html/admin/controller/extension directory, the iocartupdateinventory.php file is missing.

Image

I take this as a hint that my files are not being loaded into the project as expected.

Attachments

???
Screenshot 2025-01-25 at 2.17.20 PM.png
???
Screenshot 2025-01-25 at 2.16.58 PM.png

Newbie

Posts

Joined
Sun Jan 12, 2025 1:04 am

Post by softmonke » Tue Jan 28, 2025 4:09 pm

The status displayed follows the status of the setting name: "module_extension_name_status", assuming that your extension is under "Modules" (for instance, if your extension is a shipping extension, then the status setting name should be "shipping_extension_name_status".

Therefore, in your extension's settings page, there should be a status option that toggles the setting "module_extension_name_status" (value of 1 or 0), which is stored in the "setting" table in your database.

Check out our ever-growing list of extensions for OpenCart here.
Some useful extensions for a better admin experience: Image File Manager ProDrag & Drop Sort Order

Reach out to us at hello@softmonke.com for your OpenCart web development needs or feedback for our extensions.


User avatar
Active Member

Posts

Joined
Tue May 23, 2023 4:42 am

Who is online

Users browsing this forum: Google [Bot] and 3 guests