Concrete CMS email command

User
add command in custom concreter cms package that will send email
Assistant
To add a custom command in a Concrete CMS 9 package that sends an email, follow these steps:

### ✅ 1. **Create the Command Class**
Create a new class like `SendEmailCommand.php` inside your package, for example in:

packages/my_package/src/Command/SendEmailCommand.php
<?php
namespace ConcretePackageMyPackageCommand;

use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use ConcreteCoreMailService as MailService;
use Core;

class SendEmailCommand extends Command
{
    protected static $defaultName = 'my-package:send-email';

    protected function configure()
    {
        $this->setDescription('Send a test email from your package.');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        /** @var MailService $mail */
        $mail = Core::make('mail');

        $mail->to('example@example.com');
        $mail->setSubject('Test Email from Custom Command');
        $mail->setBody('This is a test email sent from a Concrete CMS custom command.');
        $mail->from('admin@example.com', 'ConcreteCMS Bot');

        try {
            $mail->sendMail();
            $output->writeln('<info>Email sent successfully.</info>');
        } catch (Exception $e) {
            $output->writeln('<error>Failed to send email: ' . $e->getMessage() . '</error>');
        }

        return Command::SUCCESS;
    }
}

### ✅ 2. **Register the Command in Your Package**
In your `controller.php` of the package:

use ConcreteCoreConsoleApplication as ConsoleApplication;
use ConcretePackageMyPackageCommandSendEmailCommand;

public function on_start()
{
    if ($this->app->bound(ConsoleApplication::class)) {
        $this->app->extend(ConsoleApplication::class, function ($cli) {
            $cli->add($this->app->make(SendEmailCommand::class));
            return $cli;
        });
    }
}

### ✅ 3. **Run the Command**
After installing or refreshing the package, run:

./concrete/bin/concrete my-package:send-email

Let me know if you want to:
– Pass the recipient email via CLI options
– Use a queueable mail job
– Pull recipient addresses from the database

User
Packahe is
// File: packages/shopping_feed_manager/controller.php
namespace ConcretePackageShoppingFeedManager;
Assistant
Perfect. Since your package is located at:

packages/shopping_feed_manager/controller.php
namespace ConcretePackageShoppingFeedManager;

I'll now guide you on how to register a custom CLI command (e.g. `shopping-feed:send-email`) in your **Concrete CMS package** that **sends an email**.

## ✅ Step-by-Step Setup

### 📁 1. Create Command File

Create a directory `src/Command` in your package:

packages/shopping_feed_manager/src/Command/SendEmailCommand.php

Then add this content:

<?php
namespace ConcretePackageShoppingFeedManagerCommand;

use ConcreteCoreMailService as MailService;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use Core;

class SendEmailCommand extends Command
{
    protected static $defaultName = 'shopping-feed:send-email';

    protected function configure()
    {
        $this->setDescription('Sends a test email from Shopping Feed Manager package.');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        /** @var MailService $mail */
        $mail = Core::make('mail');
        $mail->to('you@example.com');
        $mail->from('admin@example.com', 'Shopping Feed Bot');
        $mail->setSubject('Shopping Feed Test Email');
        $mail->setBody('Hello, this is a test email from Shopping Feed Manager.');

        try {
            $mail->sendMail();
            $output->writeln('<info>Email sent successfully.</info>');
        } catch (Exception $e) {
            $output->writeln('<error>Error: ' . $e->getMessage() . '</error>');
        }

        return Command::SUCCESS;
    }
}

### 🧠 2. Register Command in Your Package Controller

Open:

packages/shopping_feed_manager/controller.php

And update the `on_start()` method:

<?php
namespace ConcretePackageShoppingFeedManager;

use ConcreteCorePackagePackage;
use ConcreteCoreConsoleApplication as ConsoleApplication;
use ConcretePackageShoppingFeedManagerCommandSendEmailCommand;

class Controller extends Package
{
    protected $pkgHandle = 'shopping_feed_manager';
    protected $appVersionRequired = '9.0.0';
    protected $pkgVersion = '1.0.0';

    public function getPackageName()
    {
        return t('Shopping Feed Manager');
    }

    public function getPackageDescription()
    {
        return t('Manages shopping feeds.');
    }

    public function on_start()
    {
        if ($this->app->bound(ConsoleApplication::class)) {
            $this->app->extend(ConsoleApplication::class, function ($cli) {
                $cli->add($this->app->make(SendEmailCommand::class));
                return $cli;
            });
        }
    }
}

### 🚀 3. Run the Command

After installation or refreshing the package:

./concrete/bin/concrete shopping-feed:send-email

You should see:

Email sent successfully.

Let me know if you want:
– Email recipient as a CLI argument (`–to=abc@xyz.com`)
– HTML email body
– Queue the mail job
– Log emails to a file for debug

I can update the code accordingly.

User
There are no commands defined in the "shopping-feed" namespace.
Assistant
If you're getting:

> `There are no commands defined in the "shopping-feed" namespace.`

then Concrete CMS isn't registering your custom command properly. Let's fix this **step-by-step**.

## ✅ Checklist to Debug and Fix

### ✅ 1. **Autoloading for `src/Command`**

Make sure your package is telling Concrete how to autoload classes.

In your `shopping_feed_manager/controller.php`, add this to your package class:

public function getPackageAutoloaderRegistries()
{
    return [
        'src/Command' => 'ConcretePackageShoppingFeedManagerCommand',
    ];
}

This tells Concrete to autoload your command classes.

### ✅ 2. **Confirm Folder/File Structure**

Make sure your file is here:

packages/shopping_feed_manager/src/Command/SendEmailCommand.php

And it starts with:

namespace ConcretePackageShoppingFeedManagerCommand;

And that the class is named correctly:

class SendEmailCommand extends Command

### ✅ 3. **Check Command Registration in `on_start()`**

In your `controller.php`'s `on_start()` method, it should be exactly:

use ConcreteCoreConsoleApplication as ConsoleApplication;
use ConcretePackageShoppingFeedManagerCommandSendEmailCommand;

public function on_start()
{
    if ($this->app->bound(ConsoleApplication::class)) {
        $this->app->extend(ConsoleApplication::class, function ($cli) {
            $cli->add($this->app->make(SendEmailCommand::class));
            return $cli;
        });
    }
}

### ✅ 4. **Clear Cache & Reinstall Package**

After making the changes:

./concrete/bin/concrete c5:clear-cache

If still not working, **uninstall and reinstall the package**:

./concrete/bin/concrete c5:package-uninstall shopping_feed_manager
./concrete/bin/concrete c5:package-install shopping_feed_manager

### ✅ 5. **Test It Again**

Then try:

./concrete/bin/concrete list

You should see:

shopping-feed
  shopping-feed:send-email     Sends a test email from Shopping Feed Manager package.

Then test:

./concrete/bin/concrete shopping-feed:send-email

Let me know if you're still getting the error after these exact steps — I’ll help debug further with your actual file structure.

User
still
admin1@concrete-cms-web:/var/www/html$ ./concrete/bin/concrete shopping-feed:send-email

There are no commands defined in the "shopping-feed" namespace.

Assistant
Thanks for confirming. Let's resolve this step-by-step **with a clean diagnostic**. This issue typically comes from **autoloading not working** or **command not registered properly** in `on_start()`. Here’s how to fully fix it:

## ✅ 1. **Minimal Working Example (Reproduce + Fix)**

### ✅ Create this file:

**📄 `packages/shopping_feed_manager/src/Command/SendEmailCommand.php`**

<?php
namespace ConcretePackageShoppingFeedManagerCommand;

use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;

class SendEmailCommand extends Command
{
    protected static $defaultName = 'shopping-feed:send-email';

    protected function configure()
    {
        $this->setDescription('Test email command from Shopping Feed Manager.');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('<info>Command is working ✅</info>');
        return Command::SUCCESS;
    }
}

### ✅ Update your package controller:

**📄 `packages/shopping_feed_manager/controller.php`**

<?php
namespace ConcretePackageShoppingFeedManager;

use ConcreteCorePackagePackage;
use ConcreteCoreConsoleApplication as ConsoleApplication;
use ConcretePackageShoppingFeedManagerCommandSendEmailCommand;

class Controller extends Package
{
    protected $pkgHandle = 'shopping_feed_manager';
    protected $appVersionRequired = '9.0.0';
    protected $pkgVersion = '1.0.0';

    public function getPackageName()
    {
        return t('Shopping Feed Manager');
    }

    public function getPackageDescription()
    {
        return t('Handles shopping feeds');
    }

    public function getPackageAutoloaderRegistries()
    {
        return [
            'src/Command' => 'Concrete\Package\ShoppingFeedManager\Command',
        ];
    }

    public function on_start()
    {
        if ($this->app->bound(ConsoleApplication::class)) {
            $this->app->extend(ConsoleApplication::class, function ($cli) {
                $cli->add($this->app->make(SendEmailCommand::class));
                return $cli;
            });
        }
    }
}

### ✅ Clear cache and **reinstall your package**:

./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete c5:package-uninstall shopping_feed_manager
./concrete/bin/concrete c5:package-install shopping_feed_manager

### ✅ Confirm command is now available:

./concrete/bin/concrete list | grep shopping

If you see:

shopping-feed:send-email   Test email command from Shopping Feed Manager.

Then try:

./concrete/bin/concrete shopping-feed:send-email

✅ It should print: `Command is working ✅`

### ⛔ If Still Fails

Please share the output of this:

tree packages/shopping_feed_manager

And confirm:
– PHP version (`php -v`)
– Concrete CMS version (`./concrete/bin/concrete -V`)

I’ll debug it directly from your structure.

User
admin1@concrete-cms-web:/var/www/html$ ./concrete/bin/concrete list | grep shopping
admin1@concrete-cms-web:/var/www/html$
Assistant
Thanks, this confirms that your command is still **not being registered**, meaning the `SendEmailCommand` class is not being found or added properly. Let’s fix it with full diagnostic and next steps.

## 🔍 Let’s Break It Down Step by Step

### ✅ 1. Confirm Folder and File Structure

Run this from your root (`/var/www/html`) and paste the output:

tree packages/shopping_feed_manager

I need to confirm that:

– `controller.php` is inside `packages/shopping_feed_manager/`
– `SendEmailCommand.php` is inside `packages/shopping_feed_manager/src/Command/`

### ✅ 2. Confirm Namespace and Class Match File Path

Open `SendEmailCommand.php` and check it starts like this **exactly**:

<?php
namespace ConcretePackageShoppingFeedManagerCommand;

use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;

class SendEmailCommand extends Command
{
    protected static $defaultName = 'shopping-feed:send-email';

    protected function configure()
    {
        $this->setDescription('Test email command from Shopping Feed Manager.');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('<info>Command is working ✅</info>');
        return Command::SUCCESS;
    }
}

### ✅ 3. Hardcode Class Path in Controller for Debugging

Open your `controller.php` and **temporarily replace** the command registration with this (to rule out autoloading problems):

public function on_start()
{
    if ($this->app->bound(ConsoleApplication::class)) {
        $this->app->extend(ConsoleApplication::class, function ($cli) {
            require_once __DIR__ . '/src/Command/SendEmailCommand.php';
            $cli->add(new ConcretePackageShoppingFeedManagerCommandSendEmailCommand());
            return $cli;
        });
    }
}

This hardcoded `require_once` skips autoloading and directly loads the file — great for debugging.

### ✅ 4. Clear Cache & Reinstall

./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete c5:package-uninstall shopping_feed_manager
./concrete/bin/concrete c5:package-install shopping_feed_manager

Then test again:

./concrete/bin/concrete list | grep shopping
./concrete/bin/concrete shopping-feed:send-email

### ✅ 5. Bonus: Check PHP Errors

If it’s **still not working**, run:

php -d display_errors=1 ./concrete/bin/concrete list

This may show hidden PHP errors (e.g., class not found, parse error, etc.).

Let me know:
– Output of `tree packages/shopping_feed_manager`
– Whether the hardcoded `require_once` works

Once it works, we’ll switch back to clean autoload-based registration.

User
admin1@concrete-cms-web:/var/www/html$ tree packages/shopping_feed_manager
packages/shopping_feed_manager
├── controller.php
├── controllers
│   ├── api
│   │   └── feed_api.php
│   ├── merchant_feed.php
│   └── single_page
│   └── dashboard
│   ├── shopping_feed
│   │   ├── add.php
│   │   ├── edit.php
│   │   ├── settings.php
│   │   └── tracklog.php
│   └── shopping_feed.php
├── css
│   └── admin-feed.css
├── icon.png
├── js
│   └── admin-feed.js
├── single_pages
│   └── dashboard
│   └── shopping_feed
│   ├── add.php
│   ├── edit.php
│   ├── settings.php
│   ├── tracklog.php
│   └── view.php
└── src
└── Command
├── GenerateFeedCommand.php
└── SendEmailCommand.php

13 directories, 18 files
admin1@concrete-cms-web:/v

Assistant
Perfect — your structure is **correct**. Now the issue is likely with either:

– **Autoloader not picking up your `src/Command/` classes**, or
– The **command not being correctly registered**.

Let’s get this working immediately by using a **safe fallback**: hardcoding the `require_once` to bypass autoloading.

## ✅ Fix: Use `require_once` in `on_start()` Temporarily

Edit `packages/shopping_feed_manager/controller.php` and update your `on_start()` like this:

<?php
namespace ConcretePackageShoppingFeedManager;

use ConcreteCorePackagePackage;
use ConcreteCoreConsoleApplication as ConsoleApplication;

class Controller extends Package
{
    protected $pkgHandle = 'shopping_feed_manager';
    protected $appVersionRequired = '9.0.0';
    protected $pkgVersion = '1.0.0';

    public function getPackageName()
    {
        return t('Shopping Feed Manager');
    }

    public function getPackageDescription()
    {
        return t('Handles shopping feeds');
    }

    public function on_start()
    {
        if ($this->app->bound(ConsoleApplication::class)) {
            $this->app->extend(ConsoleApplication::class, function ($cli) {
                // Directly include the command file to avoid autoload issues
                require_once __DIR__ . '/src/Command/SendEmailCommand.php';

                $cli->add(new ConcretePackageShoppingFeedManagerCommandSendEmailCommand());
                return $cli;
            });
        }
    }
}

> ✅ This **manually loads** the command file and registers it, ignoring autoloader issues.

## 🔃 Then: Refresh and Test

### Run:

./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list | grep shopping

If successful, you’ll see:

shopping-feed:send-email     Test email command from Shopping Feed Manager.

Then test the command:

./concrete/bin/concrete shopping-feed:send-email

You should get:

Command is working ✅

## ✅ Next Step: Fix Autoloader Permanently (Optional)

Once you confirm the command is working, we can clean up and properly autoload like this:

In `controller.php`:

public function getPackageAutoloaderRegistries()
{
    return [
        'src/Command' => 'Concrete\Package\ShoppingFeedManager\Command',
    ];
}

And **remove `require_once`** once confirmed autoloading works.

Let me know the output of the command after this. If this works, we’ll finalize and clean it up.

User
admin1@concrete-cms-web:/var/www/html$ ./concrete/bin/concrete list | grep shopping
admin1@concrete-cms-web:/var/www/html$
Assistant
Thanks for testing. Since your command **still does not appear**, even after using `require_once`, it strongly suggests that:

> 🔴 The `on_start()` method is **not being executed at all**, meaning the package might not be loaded properly by Concrete CMS.

Let’s **verify and fix that now.**

## ✅ Step-by-Step Final Fix Plan

### ✅ 1. **Confirm Package Is Installed and Active**

Run this:

./concrete/bin/concrete c5:package-list

Look for:

shopping_feed_manager (Shopping Feed Manager)

If it's not listed or shows as *uninstalled*, install it:

./concrete/bin/concrete c5:package-install shopping_feed_manager

If already installed, **uninstall and reinstall** to trigger `on_start()`:

./concrete/bin/concrete c5:package-uninstall shopping_feed_manager
./concrete/bin/concrete c5:package-install shopping_feed_manager

### ✅ 2. **Put Debug Output in `on_start()`**

To confirm `on_start()` is even running, modify `controller.php` like this:

public function on_start()
{
    file_put_contents('/tmp/c5_onstart.log', "on_start() hit at " . date('Y-m-d H:i:s') . "n", FILE_APPEND);

    if ($this->app->bound(ConsoleApplication::class)) {
        $this->app->extend(ConsoleApplication::class, function ($cli) {
            require_once __DIR__ . '/src/Command/SendEmailCommand.php';
            $cli->add(new ConcretePackageShoppingFeedManagerCommandSendEmailCommand());
            return $cli;
        });
    }
}

Then run:

./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete

Then check if log file was created:

cat /tmp/c5_onstart.log

### 🔁 If `/tmp/c5_onstart.log` is **not created**:

That confirms Concrete CMS is **not loading your package**.

**Fix:** Rename your package folder to use **lowercase with underscores only**.

mv packages/shopping_feed_manager packages/shopping_feed_manager_fixed

Update the class in `controller.php`:

namespace ConcretePackageShoppingFeedManagerFixed;
class Controller extends Package
{
    protected $pkgHandle = 'shopping_feed_manager_fixed';
    ...

Then reinstall:

./concrete/bin/concrete c5:package-uninstall shopping_feed_manager
./concrete/bin/concrete c5:package-install shopping_feed_manager_fixed

Then re-run:

./concrete/bin/concrete list | grep shopping

### ✅ Once It Appears

You’ll see:

shopping-feed:send-email     Test email command from Shopping Feed Manager.

Let me know what happens with:
– `package-list`
– `/tmp/c5_onstart.log`
– and `list | grep shopping`

We’ll finalize from there.

User
admin1@concrete-cms-web:/var/www/html$ ./concrete/bin/concrete list | grep shopping
admin1@concrete-cms-web:/var/www/html$
Assistant
Thanks. Since you're **still not seeing the command**, and assuming you already:

– Added the `require_once` in `on_start()`
– Added `file_put_contents('/tmp/c5_onstart.log', …)` for debugging
– Cleared cache and reinstalled the package

But **`/tmp/c5_onstart.log` was NOT created**, then we can now **conclude:**

> 🔥 **Concrete CMS is not loading your package at all**, or it’s failing silently.

Let’s fix this once and for all.

## ✅ FINAL FIX PLAN

### ✅ 1. Rename the Package Folder (100% Critical in Concrete CMS)

Concrete CMS is **case-sensitive and naming-strict**.

Please rename the folder to match the expected package handle exactly.

Run this:

mv packages/shopping_feed_manager packages/shopping_feed_manager_fixed

Now update the `controller.php`:

<?php
namespace ConcretePackageShoppingFeedManagerFixed;

use ConcreteCorePackagePackage;
use ConcreteCoreConsoleApplication as ConsoleApplication;

class Controller extends Package
{
    protected $pkgHandle = 'shopping_feed_manager_fixed';
    protected $appVersionRequired = '9.0.0';
    protected $pkgVersion = '1.0.0';

    public function getPackageName()
    {
        return t('Shopping Feed Manager');
    }

    public function getPackageDescription()
    {
        return t('Handles shopping feeds');
    }

    public function on_start()
    {
        // Debug log to confirm this is working
        file_put_contents('/tmp/c5_onstart.log', "on_start() hit at " . date('Y-m-d H:i:s') . "n", FILE_APPEND);

        if ($this->app->bound(ConsoleApplication::class)) {
            $this->app->extend(ConsoleApplication::class, function ($cli) {
                require_once __DIR__ . '/src/Command/SendEmailCommand.php';
                $cli->add(new ConcretePackageShoppingFeedManagerFixedCommandSendEmailCommand());
                return $cli;
            });
        }
    }
}

Also update the namespace in your command:

// src/Command/SendEmailCommand.php
namespace ConcretePackageShoppingFeedManagerFixedCommand;

### ✅ 2. Reinstall Package Completely

./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete c5:package-uninstall shopping_feed_manager_fixed || true
./concrete/bin/concrete c5:package-install shopping_feed_manager_fixed

### ✅ 3. Test If It Works

cat /tmp/c5_onstart.log
./concrete/bin/concrete list | grep shopping
./concrete/bin/concrete shopping-feed:send-email

You should now get:

Command is working ✅

If this still fails after following these exact steps, I will give you a **minimal sample working package zip** to upload and try instead. But this should fix it.

Please confirm results of:
– `cat /tmp/c5_onstart.log`
– `./concrete/bin/concrete list | grep shopping`

User
./concrete/bin/concrete list | grep shopping
Still not showing custom command
Assistant
Thanks Neeraj. Since it’s **still not working** after all cleanups, it's time to **remove all doubt** by starting with a **minimal, working package** that:

– ✅ Registers a CLI command successfully
– ✅ Outputs proof via log
– ✅ Uses no autoload complications

## 🧪 ✅ Step-by-Step: Minimal Working Concrete CMS CLI Command Package

### 🧱 1. Create Folder

mkdir -p packages/test_cli/src/Command

### 🧾 2. Create the Controller

**📄 `packages/test_cli/controller.php`**

<?php
namespace ConcretePackageTestCli;

use ConcreteCorePackagePackage;
use ConcreteCoreConsoleApplication as ConsoleApplication;

class Controller extends Package
{
    protected $pkgHandle = 'test_cli';
    protected $appVersionRequired = '9.0.0';
    protected $pkgVersion = '1.0.0';

    public function getPackageName()
    {
        return t('Test CLI');
    }

    public function getPackageDescription()
    {
        return t('Testing custom CLI command');
    }

    public function on_start()
    {
        file_put_contents('/tmp/testcli_onstart.log', "✔ on_start() calledn", FILE_APPEND);

        if ($this->app->bound(ConsoleApplication::class)) {
            $this->app->extend(ConsoleApplication::class, function ($cli) {
                require_once __DIR__ . '/src/Command/TestCommand.php';
                $cli->add(new ConcretePackageTestCliCommandTestCommand());
                return $cli;
            });
        }
    }
}

### 🧾 3. Create the CLI Command

**📄 `packages/test_cli/src/Command/TestCommand.php`**

<?php
namespace ConcretePackageTestCliCommand;

use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;

class TestCommand extends Command
{
    protected static $defaultName = 'test:cli';

    protected function configure()
    {
        $this->setDescription('Test CLI command');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('<info>✅ TestCommand is working!</info>');
        return Command::SUCCESS;
    }
}

### 🧪 4. Install and Run

./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete c5:package-install test_cli

Then test:

cat /tmp/testcli_onstart.log
./concrete/bin/concrete list | grep test
./concrete/bin/concrete test:cli

### ✅ Expected Output

✔ on_start() called
test:cli       Test CLI command
✅ TestCommand is working!

### ✅ Next

Once this works:
– Go back to your `shopping_feed_manager`
– Copy this working structure
– Replace `TestCommand` with your `SendEmailCommand`

Would you like me to generate a `.zip` of this working package so you can upload and test directly?

Leave a Comment

Your email address will not be published. Required fields are marked *