Create custom command CMS
August 7, 2025 Β· AI Conversations
1. **Create the command class** (extends `SymfonyComponentConsoleCommandCommand`)
2. **Register the command** in your packageβs `on_start` method using the `ConsoleApplication` binding.
—
### β Step-by-step guide
#### π Folder structure (example):
packages/
βββ your_package_handle/
βββ controller.php
βββ src/
βββ Command/
βββ GenerateFeedCommand.php
—
### π§± 1. Create the Command Class
**File:** `packages/your_package_handle/src/Command/GenerateFeedCommand.php`
<?php
namespace YourPackageNamespaceCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class GenerateFeedCommand extends Command
{
protected static $defaultName = 'feed:generate';
protected function configure()
{
$this->setDescription('Generates the product feed.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// Your logic here
$output->writeln('<info>Feed generated successfully!</info>');
return Command::SUCCESS;
}
}
> π Replace `YourPackageNamespace` with your actual package namespace.
—
### π§© 2. Register the Command in `controller.php`
use ConcreteCoreConsoleApplication as ConsoleApplication;
use YourPackageNamespaceCommandGenerateFeedCommand;
public function on_start()
{
$this->app->extend(ConsoleApplication::class, function (ConsoleApplication $cli) {
$cli->add($this->app->make(GenerateFeedCommand::class));
return $cli;
});
}
—
### βοΈ 3. Add Autoloading in `composer.json`
In your packageβs `composer.json`:
"autoload": {
"psr-4": {
"YourPackageNamespace\": "src/"
}
}
Then run:
composer dump-autoload
—
### β 4. Test Your Command
Run this in your Concrete CMS project root:
./concrete/bin/concrete list
You should see `feed:generate` listed.
Run it:
./concrete/bin/concrete feed:generate
—
If the command still doesnβt show:
– Check if your package is installed and enabled.
– Ensure there's no typo in namespace or command registration.
– Double check `composer dump-autoload` was run from the correct path (`packages/your_package_handle/`).
Would you like me to generate a ready-made example package with this command included?
Class "CommandGenerateFeedCommand" does not exist
means your namespace or autoloading for the command class is not correctly set up.
—
## β Fix Instructions
You need to:
1. **Correct the namespace and file location**
2. **Ensure the command is autoloaded via `composer.json`**
3. **Update the `use` statement**
—
### β 1. Correct File Path & Namespace
Create this file:
**`packages/shopping_feed_manager/src/Command/GenerateFeedCommand.php`**
<?php
namespace ConcretePackageShoppingFeedManagerCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class GenerateFeedCommand extends Command
{
protected static $defaultName = 'feed:generate';
protected function configure()
{
$this->setDescription('Generates the Google Merchant feed.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('<info>Feed generation completed successfully.</info>');
return Command::SUCCESS;
}
}
—
### β 2. Register Autoload in `composer.json`
In your package root (`packages/shopping_feed_manager/composer.json`), add:
{
"autoload": {
"psr-4": {
"Concrete\Package\ShoppingFeedManager\": "src/"
}
}
}
Then run in terminal:
cd packages/shopping_feed_manager
composer dump-autoload
—
### β 3. Update `use` Statement in `controller.php`
Change this:
use CommandGenerateFeedCommand;
To this:
use ConcretePackageShoppingFeedManagerCommandGenerateFeedCommand;
—
### β 4. Clear Cache
After making changes, run:
./concrete/bin/concrete c5:clear-cache
—
### β 5. Verify Command Exists
Run this:
./concrete/bin/concrete list | grep feed
You should see:
feed:generate Generates the Google Merchant feed.
—
Let me know if you want to pass options like `–id=3` to this command or save log to database.
No composer.json in current directory
—
### β Solution: Define autoload in the *main* projectβs `composer.json`
Since Concrete CMS loads packages via the main application, do this instead:
—
### β Step 1: Use correct namespace structure
Ensure your file is located at:
/var/www/html/packages/shopping_feed_manager/src/Command/GenerateFeedCommand.php
And starts with:
namespace ConcretePackageShoppingFeedManagerCommand;
β Youβve already done this part β good.
—
### β Step 2: Edit the main `composer.json`
Open:
/var/www/html/composer.json
Find the `"autoload"` section and add this:
"autoload": {
"psr-4": {
"Concrete\Package\ShoppingFeedManager\": "packages/shopping_feed_manager/src/"
}
}
—
### β Step 3: Dump autoload from project root
Now run this from the **project root**, not inside the package:
cd /var/www/html
composer dump-autoload
—
### β Step 4: Clear Concrete CMS cache
./concrete/bin/concrete c5:clear-cache
—
### β Step 5: Test command
./concrete/bin/concrete list | grep feed
Expected output:
feed:generate Generates the Google Merchant feed.
—
Let me know if you want this command to accept arguments like `–slug=demo` or log its results.
Generating autoload files
composer/package-versions-deprecated: Generating version class…
composer/package-versions-deprecated: …done generating version class
Generated autoload files
admin1@concrete-cms-web:/var/www/html$ ./concrete/bin/concrete c5:clear-cache
Clearing the cache… done.
admin1@concrete-cms-web:/var/www/html$ ./concrete/bin/concrete list | grep feed
admin1@concrete-cms-web:/var/www/html$
{
"name": "concrete5/concrete5",
"license": "MIT",
"description": "Concrete β an open source content management system.",
"type": "project",
"keywords": [ "concrete5", "CMS", "concreteCMS", "concrete" ],
"minimum-stability": "stable",
"prefer-stable": true,
"archive": {
"exclude": [
"/tests", "/build", "/.github", "/.gitattributes", "/CONTRIBUTING.md", "/phpunit.xml"
]
},
"require": {
"wikimedia/composer-merge-plugin": "~1.3|~2.0.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.19.2",
"phpunit/phpunit": "^8.5.20",
"mockery/mockery": "^1.3.4",
"mlocati/ci-info": "^1"
},
"config": {
"process-timeout": 0,
"vendor-dir": "./concrete/vendor",
"platform": {
"php": "7.3"
},
"allow-plugins": {
"composer/package-versions-deprecated": true,
"mlocati/composer-patcher": true,
"wikimedia/composer-merge-plugin": true
}
},
"replace": {
"concrete5/core": "self.version",
"laminas/laminas-cache-storage-adapter-apc": "*",
"laminas/laminas-cache-storage-adapter-apcu": "*",
"laminas/laminas-cache-storage-adapter-blackhole": "*",
"laminas/laminas-cache-storage-adapter-dba": "*",
"laminas/laminas-cache-storage-adapter-ext-mongodb": "*",
"laminas/laminas-cache-storage-adapter-filesystem": "*",
"laminas/laminas-cache-storage-adapter-memcache": "*",
"laminas/laminas-cache-storage-adapter-memcached": "*",
"laminas/laminas-cache-storage-adapter-mongodb": "*",
"laminas/laminas-cache-storage-adapter-redis": "*",
"laminas/laminas-cache-storage-adapter-session": "*",
"laminas/laminas-cache-storage-adapter-wincache": "*",
"laminas/laminas-cache-storage-adapter-xcache": "*",
"laminas/laminas-cache-storage-adapter-zend-server": "*"
},
"extra": {
"r1": "This is using the wikimedia plugin to merge our core/composer.json file.",
"r2": "By doing this, we greatly simplify the requirements for setting up a subtree split",
"merge-plugin": {
"include": [
"concrete/composer.json"
],
"recurse": false,
"replace": false,
"merge-extra": false
},
"allow-subpatches": [
"concretecms/dependency-patches"
]
},
"autoload": {
"psr-4": {
"Concrete\Package\ShoppingFeedManager\": "packages/shopping_feed_manager/src/"
}
},
"autoload-dev": {
"psr-4": {
"Concrete\Tests\": "tests/tests/",
"Concrete\TestHelpers\": "tests/helpers/"
}
},
"scripts": {
"test": "phpunit",
"post-create-project-cmd": [
"composer config –unset platform.php"
]
}
}
—
## β Checklist to Debug Why Command Isnβt Detected
### 1. β
Confirm File Exists
Make sure this file **physically exists**:
packages/shopping_feed_manager/src/Command/GenerateFeedCommand.php
And contains:
namespace ConcretePackageShoppingFeedManagerCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class GenerateFeedCommand extends Command
{
protected static $defaultName = 'feed:generate';
protected function configure()
{
$this->setDescription('Generates the Google Merchant feed.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('<info>Feed generation completed.</info>');
return Command::SUCCESS;
}
}
—
### 2. β Correct `use` Statement in `controller.php`
Inside your package controller:
use ConcretePackageShoppingFeedManagerCommandGenerateFeedCommand;
Not:
use CommandGenerateFeedCommand;
—
### 3. β Add `dump-autoload` log line (optional)
Add this line **just above `$cli->add(…)`** to ensure itβs hitting that line:
Log::info("Registering GenerateFeedCommand");
Then check the logs:
tail -f application/files/logs/*.log
If this doesnβt appear when running:
./concrete/bin/concrete list
Then the code inside `on_start()` isn't being hit β maybe the package is not **installed/enabled**.
—
### 4. β Check if package is enabled
In dashboard, go to:
**Dashboard β Extend Concrete**
Ensure `Shopping Feed Manager` is **installed and enabled**.
If not, run:
./concrete/bin/concrete c5:package-install shopping_feed_manager
—
### 5. β Add Debug Output in CLI Extension
Update this to confirm the command is loading:
$this->app->extend(ConsoleApplication::class, function (ConsoleApplication $cli) {
Log::info("Adding GenerateFeedCommand to CLI");
$cli->add($this->app->make(GenerateFeedCommand::class));
return $cli;
});
Then re-run:
./concrete/bin/concrete list
And check logs again:
tail -f application/files/logs/*.log
—
### β Final Confirmation Command
./concrete/bin/concrete feed:generate
You should see:
Feed generation completed.
—
Let me know what your `GenerateFeedCommand.php` contains β I can confirm if the class is valid.
There are no commands defined in the "feed" namespace.
admin1@concrete-cms-web:/var/www/html$
There are no commands defined in the "feed" namespace."
it means the CLI doesnβt recognize the `feed:generate` command at all β most likely because it's not being registered properly.
—
### β Let's isolate and fix it step-by-step
—
## π Step 1: Confirm `GenerateFeedCommand.php` content
Please confirm this exact file exists:
/var/www/html/packages/shopping_feed_manager/src/Command/GenerateFeedCommand.php
And it contains **this exact code**:
<?php
namespace ConcretePackageShoppingFeedManagerCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class GenerateFeedCommand extends Command
{
protected static $defaultName = 'feed:generate';
protected function configure()
{
$this->setDescription('Generates the Google Merchant feed.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('<info>Feed generated successfully!</info>');
return Command::SUCCESS;
}
}
—
## π Step 2: Confirm this `use` line is in `controller.php`
In your package controller (`controller.php`):
use ConcretePackageShoppingFeedManagerCommandGenerateFeedCommand;
—
## π Step 3: Add log to check if itβs being hit
In `controller.php`, inside the `on_start()` method, temporarily add:
Log::info("β
Registering GenerateFeedCommand...");
$this->app->extend(ConsoleApplication::class, function (ConsoleApplication $cli) {
Log::info("β
Inside CLI extender");
$cli->add($this->app->make(GenerateFeedCommand::class));
return $cli;
});
Then run:
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list
Now check the log file:
tail -n 50 application/files/logs/*.log
π **If these logs do not appear**, your package might not be **bootstrapping at all**.
—
## β Final Check: Is the package installed?
Run this command to list installed packages:
./concrete/bin/concrete c5:package-list
If `shopping_feed_manager` is **not installed**, install it:
./concrete/bin/concrete c5:package-install shopping_feed_manager
If already installed but old, upgrade it:
./concrete/bin/concrete c5:package-update shopping_feed_manager
Then:
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list | grep feed
—
## β What to share if still broken
If it's still not working, please share:
– Output of `ls -l packages/shopping_feed_manager/src/Command/`
– Output of `grep -i generatefeedcommand packages/shopping_feed_manager/controller.php`
– Any log output from `application/files/logs/` after running `concrete list`
That way I can spot exactly where the issue is.
total 8
-rw-rw-r– 1 admin1 admin1 2442 Jun 16 12:26 GenerateFeedCommand.php
-rw-rw-r– 1 admin1 admin1 1202 Jun 16 12:04 SendEmailCommand.php
admin1@concrete-cms-web:/var/www/html$ grep -i generatefeedcommand packages/shopping_feed_manager/controller.php
use ConcretePackageShoppingFeedManagerCommandGenerateFeedCommand;
Log::info("Registering GenerateFeedCommand");
$cli->add($this->app->make(GenerateFeedCommand::class));
admin1@concrete-cms-web:/var/www/html$
β
`GenerateFeedCommand.php` **exists**
β
Namespace and `use` statement are **correct**
β
You have logging (`Registering GenerateFeedCommand`) **before `$cli->add(…)`**
But you are **not seeing anything in the logs**, and `feed:generate` still does **not appear in the command list**.
This strongly suggests the **`on_start()` is not being triggered**, which happens when **the package is installed but not enabled**, or **youβre testing changes without upgrading the package**.
—
## β Final Fixes Checklist
### β 1. Reinstall or upgrade the package
From the project root:
./concrete/bin/concrete c5:package-list
If `shopping_feed_manager` is **not listed**, install it:
./concrete/bin/concrete c5:package-install shopping_feed_manager
If it is listed but may have changed:
./concrete/bin/concrete c5:package-update shopping_feed_manager
Then clear the cache:
./concrete/bin/concrete c5:clear-cache
And try again:
./concrete/bin/concrete list | grep feed
—
### β 2. Check that `controller.php` is in this path:
packages/shopping_feed_manager/controller.php
Not accidentally in `src/` or nested.
—
### β 3. Optional: Add `boot()` to verify load
Try replacing `on_start()` with `boot()` to confirm your class is being loaded:
public function boot()
{
Log::info("π ShoppingFeedManager booted");
}
Then:
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list
tail -n 50 application/files/logs/*.log
If `boot()` also doesn't log anything, itβs 100% a problem with the package not being installed or updated.
—
If you do all the above and still donβt see `feed:generate`, I can give you a working minimal test package (`.zip`) to confirm environment setup. Just let me know.
Command "c5:package-list" is not defined.
Did you mean one of these?
c5:boards:refresh
c5:clear-cache
c5:compare-schema
c5:config
c5:database:charset:set
c5:database:foreignkey:fix
c5:denylist:clear
c5:entities:refresh
c5:exec
c5:express:export
c5:files:generate-identifiers
c5:ide-symbols
c5:info
c5:install
c5:install-language
c5:install-package
c5:is-installed
c5:job
c5:language-install
c5:pack-package
c5:package-install
c5:package-pack
c5:package-translate
c5:package-uninstall
c5:package-update
c5:package:install
c5:package:pack
c5:package:translate
c5:package:uninstall
c5:package:update
c5:phpcs
c5:rescan-files
c5:reset
c5:service
c5:sitemap:generate
c5:theme:install
c5:translate-package
c5:uninstall-package
c5:update
c5:update-package
c5:user-group:bulk-assign-users
—
### β Check if your package is installed
Instead of `c5:package-list`, use:
./concrete/bin/concrete c5:info
This will show **installed packages** at the bottom.
Look for:
Installed Packages:
- Shopping Feed Manager (shopping_feed_manager)
—
## β If the package is NOT installed
Install it:
./concrete/bin/concrete c5:package:install shopping_feed_manager
—
## π If already installed, update it
To apply your updated `controller.php` logic (e.g., CLI registration):
./concrete/bin/concrete c5:package:update shopping_feed_manager
Then clear cache:
./concrete/bin/concrete c5:clear-cache
—
## β Final test
./concrete/bin/concrete list | grep feed
You should now see:
feed:generate Generates the Google Merchant feed.
—
Let me know if you see your package listed in `c5:info`, and I can confirm next steps.
# Hostname
concrete-cms-web
# System User
admin1
# Environment
production
# Version
Installed – Yes
Core Version – 9.4.0
Version Installed – 9.4.0
Database Version – 20250227155410
# Database Information
Version – 10.11.11-MariaDB-ubu2204-log
SQL Mode – STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
Character Set – utf8mb4
Collation – utf8mb4_unicode_ci
# Paths
Web root – /var/www/html
Core root – /var/www/html/concrete
# Packages
Community Store (2.7.5-alpha1), Shopping Feed Manager (1.0.0)
# Overrides
None
# Cache Settings
Block Cache – Off
Overrides Cache – Off
Full Page Caching – Off
Full Page Cache Lifetime – Every 6 hours (default setting).
# Database Entities Settings
Doctrine Development Mode – Off
# Server API
cli
# PHP Version
8.3.21
# PHP Extensions
apcu, bcmath, bz2, calendar, Core, ctype, curl, date, dom, exif, FFI, fileinfo, filter, ftp, gd, gettext, hash, iconv, igbinary, imagick, intl, json, ldap, libxml, mbstring, memcached, msgpack, mysqli, mysqlnd, openssl, pcntl, pcre, PDO, pdo_mysql, pdo_pgsql, pdo_sqlite, pgsql, Phar, posix, random, readline, redis, Reflection, session, shmop, SimpleXML, soap, sockets, sodium, SPL, sqlite3, standard, sysvmsg, sysvsem, sysvshm, tokenizer, uploadprogress, xml, xmlreader, xmlrpc, xmlwriter, xsl, Zend OPcache, zip, zlib
# PHP Settings
max_execution_time – 0
allow_url_fopen – On
allow_url_include – Off
error_log – no value
file_uploads – On
max_file_uploads – 20
max_input_nesting_level – 64
max_input_time – -1
max_input_vars – 5000
max_multipart_body_parts – -1
memory_limit – -1
post_max_size – 100M
short_open_tag – Off
upload_max_filesize – 100M
zend.exception_string_param_max_len – 0
zend.max_allowed_stack_size – 0
timelib version – 2022.12
iconv library version – 2.36
igbinary version – 3.2.16
imagick module version – 3.8.0
Imagick compiled with ImageMagick version – ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25 https://imagemagick.org
Imagick using ImageMagick library version – ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25 https://imagemagick.org
imagick.skip_version_check – 1
ICU version – 72.1
ICU Data version – 72.1
ICU TZData version – 2022e
ICU Unicode version – 15.0
ldap.max_links – Unlimited
libmbfl version – 1.3.2
Multibyte regex (oniguruma) version – 6.9.8
mbstring.regex_retry_limit – 1000000
mbstring.regex_stack_limit – 100000
libmemcached-awesome version – 1.1.4
memcached.item_size_limit – 0
memcached.sess_lock_max_wait – not set
memcached.sess_lock_wait_max – 150
memcached.sess_server_failure_limit – 0
Client API library version – mysqlnd 8.3.21
mysqli.max_links – Unlimited
mysqli.max_persistent – Unlimited
pcre.backtrack_limit – 1000000
pcre.recursion_limit – 100000
Client API version – mysqlnd 8.3.21
pgsql.max_links – Unlimited
pgsql.max_persistent – Unlimited
Phar API version – 1.1.1
redis.pconnect.connection_limit – 0
session.cache_limiter – nocache
session.gc_maxlifetime – 1440
soap.wsdl_cache_limit – 5
libsodium headers version – 1.0.18
libsodium library version – 1.0.18
unserialize_max_depth – 4096
XMLRPC extension version – 1.0.0RC3
core library version – xmlrpc-epi v. 0.54
opcache.jit_bisect_limit – 0
opcache.jit_max_exit_counters – 8192
opcache.jit_max_loop_unrolls – 8
opcache.jit_max_polymorphic_calls – 2
opcache.jit_max_recursive_calls – 2
opcache.jit_max_recursive_returns – 2
opcache.jit_max_root_traces – 1024
opcache.jit_max_side_traces – 128
opcache.jit_max_trace_length – 1024
opcache.max_accelerated_files – 1000000
opcache.max_file_size – 0
opcache.max_wasted_percentage – 5
Zip version – 1.22.3
Libzip version – 1.7.3
β
Your package **Shopping Feed Manager (1.0.0)** is installed
β
So `on_start()` **should be firing**, and CLI command registration should work
Yet `feed:generate` still doesnβt appear, which means the issue is likely **inside the `GenerateFeedCommand` class registration or construction**.
Letβs **verify the autoload and instantiation fully works**:
—
## β Final Diagnosis Steps
### π§ͺ Step 1: Dump class map to verify it's autoloaded
Run this from your project root:
vendor/bin/classmap-generator packages/shopping_feed_manager/src/
You should see:
Found class: ConcretePackageShoppingFeedManagerCommandGenerateFeedCommand
If not, something is wrong with the file path or namespace.
—
### π§ͺ Step 2: Add a simple test command and debug instantiation
In `controller.php`, modify the `on_start()` to debug instantiation:
use ConcretePackageShoppingFeedManagerCommandGenerateFeedCommand;
use ConcreteCoreConsoleConsoleAwareInterface;
public function on_start()
{
Log::info("π¦ on_start triggered in Shopping Feed Manager");
$this->app->extend(ConsoleApplication::class, function (ConsoleApplication $cli) {
Log::info("βοΈ Inside CLI extender");
try {
$command = $this->app->make(GenerateFeedCommand::class);
Log::info("β
GenerateFeedCommand instantiated");
$cli->add($command);
} catch (Exception $e) {
Log::error("β Failed to instantiate command: " . $e->getMessage());
}
return $cli;
});
}
Then:
./concrete/bin/concrete list
And:
tail -n 50 application/files/logs/*.log
—
### β Step 3: If you see an error like `Unresolvable dependency`, change constructor
Make sure your `GenerateFeedCommand` **has no constructor dependencies**, or inject them properly.
This works:
public function __construct()
{
parent::__construct();
}
This fails if dependencies are not bound:
public function __construct(MyService $service)
—
### β Step 4: If logs show success but command still missing
Try renaming the command from:
protected static $defaultName = 'feed:generate';
To:
protected function configure()
{
$this->setName('feed:generate');
$this->setDescription('...');
}
Some Concrete CMS versions (esp. pre-9.5) may not support `static $defaultName`.
—
Once youβve tested the above, let me know:
1. Do logs show the command instantiating or erroring?
2. Does the command appear in `./concrete/bin/concrete list`?
Iβll help you go all the way till this is 100% working.
bash: vendor/bin/classmap-generator: No such file or directory
admin1@concrete-cms-web:/var/www/html$
Since everything else is in place, and the command is **still not showing**, the final root causes are almost certainly one of:
—
## π₯ Top Remaining Causes
### β 1. `GenerateFeedCommand` class is failing to instantiate
Please **wrap the instantiation in a `try-catch` block** to log any hidden errors.
In `controller.php`, replace this block:
$cli->add($this->app->make(GenerateFeedCommand::class));
with:
try {
Log::info("π Attempting to load GenerateFeedCommand...");
$command = $this->app->make(ConcretePackageShoppingFeedManagerCommandGenerateFeedCommand::class);
Log::info("β
Command instantiated: " . get_class($command));
$cli->add($command);
} catch (Throwable $e) {
Log::error("β Could not register GenerateFeedCommand: " . $e->getMessage());
}
Then:
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list
tail -n 100 application/files/logs/*.log
This will **tell us exactly** why the command is failing β either:
– Class not found
– Dependency error
– Registration silently failing
—
### β 2. Make sure your `GenerateFeedCommand` looks like this
<?php
namespace ConcretePackageShoppingFeedManagerCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class GenerateFeedCommand extends Command
{
protected function configure()
{
$this->setName('feed:generate');
$this->setDescription('Generates the Google Merchant feed.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('<info>Feed generated!</info>');
return Command::SUCCESS;
}
}
> β Use `$this->setName()` instead of `protected static $defaultName` for full Concrete CMS 9.4 compatibility.
—
### β 3. If still broken β create a test dummy command
Make a second dummy command to test if *any* command works:
**File:** `packages/shopping_feed_manager/src/Command/DummyCommand.php`
<?php
namespace ConcretePackageShoppingFeedManagerCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class DummyCommand extends Command
{
protected function configure()
{
$this->setName('dummy:test')
->setDescription('Test dummy command');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('<info>Dummy command executed</info>');
return Command::SUCCESS;
}
}
Then register it like this:
use ConcretePackageShoppingFeedManagerCommandDummyCommand;
$this->app->extend(ConsoleApplication::class, function (ConsoleApplication $cli) {
$cli->add(new DummyCommand());
return $cli;
});
Then test:
./concrete/bin/concrete dummy:test
If **this works**, the issue is isolated to `GenerateFeedCommand`. If **this also fails**, the issue is likely in your registration logic.
—
Let me know what the logs show after wrapping it in `try-catch` and if you'd like me to build a working test `.zip` package for comparison.
SymfonyComponentErrorHandlerErrorClassNotFoundError^ {#1484
#message: """
Attempted to load class "DummyCommand" from namespace "ConcretePackageShoppingFeedManagerCommand".n
Did you forget a "use" statement for another namespace?
"""
#code: 0
#file: "./packages/shopping_feed_manager/controller.php"
#line: 40
trace: {
./packages/shopping_feed_manager/controller.php:40 {
ConcretePackageShoppingFeedManagerController->ConcretePackageShoppingFeedManager{closure}^
βΊ $this->app->extend(ConsoleApplication::class, function (ConsoleApplication $cli) {
βΊ $cli->add(new DummyCommand());
βΊ return $cli;
}
./concrete/vendor/illuminate/container/Container.php:767 { β¦}
./concrete/vendor/illuminate/container/Container.php:694 { β¦}
./concrete/vendor/illuminate/container/Container.php:609 { β¦}
./concrete/vendor/illuminate/container/Container.php:456 { β¦}
./packages/shopping_feed_manager/controller.php:42 { β¦}
./concrete/src/Application/Application.php:273 { β¦}
./concrete/src/Foundation/Runtime/Run/CLIRunner.php:85 { β¦}
./concrete/src/Foundation/Runtime/DefaultRuntime.php:102 { β¦}
./concrete/dispatcher.php:45 { β¦}
./concrete/bin/concrete:85 { β¦}
}
}
> π₯ `Attempted to load class "DummyCommand" from namespace "ConcretePackageShoppingFeedManagerCommand"`
> β
But Concrete CMS can't find that class β itβs **not autoloaded**.
—
## β Solution: Fix the autoload setup
### β Step 1: Check file path and name
Ensure this file exists:
/var/www/html/packages/shopping_feed_manager/src/Command/DummyCommand.php
And it starts with:
<?php
namespace ConcretePackageShoppingFeedManagerCommand;
—
### β Step 2: Confirm project-level `composer.json` has this:
In `/var/www/html/composer.json`:
"autoload": {
"psr-4": {
"Concrete\Package\ShoppingFeedManager\": "packages/shopping_feed_manager/src/"
}
}
Then from `/var/www/html/` (project root), run:
composer dump-autoload
—
### β Step 3: Clear cache and retest
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list
Then try:
./concrete/bin/concrete dummy:test
—
### β Optional: Log confirmation
In `DummyCommand.php`, temporarily add:
public function __construct()
{
Log::info("π’ DummyCommand constructor called");
parent::__construct();
}
Then run again and check:
tail -n 50 application/files/logs/*.log
—
## β Summary of What to Do Now
1. Confirm file path: `src/Command/DummyCommand.php`
2. Confirm correct namespace in that file
3. Confirm project-level autoload path includes:
`"Concrete\Package\ShoppingFeedManager\": "packages/shopping_feed_manager/src/"`
4. Run `composer dump-autoload` from **project root**
5. Clear cache and retry the command
—
Let me know once you run `composer dump-autoload` and `dummy:test` β if that works, your original `GenerateFeedCommand` will work too.
There are no commands defined in the "dummy" namespace.
This time you're seeing:
> β "There are no commands defined in the `dummy` namespace"
This means:
β
The `DummyCommand` class is now **found and autoloaded**
β But itβs **not being registered with the CLI** β meaning the `add()` method didnβt execute
—
## β Final Fix: Make sure registration code runs
In your package's `controller.php`, you must be registering the command like this:
### β Working Code for Registration (inside `on_start()`):
use ConcreteCoreConsoleApplication as ConsoleApplication;
use ConcretePackageShoppingFeedManagerCommandDummyCommand;
public function on_start()
{
Log::info("π Registering DummyCommand...");
$this->app->extend(ConsoleApplication::class, function (ConsoleApplication $cli) {
Log::info("βοΈ Inside CLI extender");
try {
$cli->add(new DummyCommand());
Log::info("β
DummyCommand added");
} catch (Throwable $e) {
Log::error("β Failed to add DummyCommand: " . $e->getMessage());
}
return $cli;
});
}
Then run:
composer dump-autoload
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list | grep dummy
tail -n 100 application/files/logs/*.log
—
### π If no log lines appear, then:
It means your `on_start()` isnβt being triggered β possible causes:
1. You renamed `on_start()` to `boot()` but didnβt update logic
2. You didn't upgrade the package after changes
Fix it by running:
./concrete/bin/concrete c5:package:update shopping_feed_manager
./concrete/bin/concrete c5:clear-cache
—
## β What you should see now
1. In the logs:
π Registering DummyCommand...
βοΈ Inside CLI extender
β
DummyCommand added
2. In the CLI:
./concrete/bin/concrete list | grep dummy
Output:
dummy:test Test dummy command
3. Running the command:
./concrete/bin/concrete dummy:test
Output:
Dummy command executed
—
Let me know the output of your logs (`tail`) and `list | grep dummy` after above steps. Once this works, your `feed:generate` command will also work the same way.
[concretecms@newserver public_html]$
Letβs **zero in and fix this for good**. Here's a reliable step-by-step:
—
## β Step-by-Step: Confirm `on_start()` is Executing
### β 1. Add logging at the very top of `on_start()` in `controller.php`:
public function on_start()
{
Log::info("π₯ on_start() of Shopping Feed Manager is running");
$this->app->extend(ConcreteCoreConsoleApplication::class, function ($cli) {
Log::info("βοΈ Inside ConsoleApplication extender");
try {
$cli->add(new ConcretePackageShoppingFeedManagerCommandDummyCommand());
Log::info("β
DummyCommand registered");
} catch (Throwable $e) {
Log::error("β Failed to register DummyCommand: " . $e->getMessage());
}
return $cli;
});
}
—
### β 2. Re-run everything cleanly
composer dump-autoload
./concrete/bin/concrete c5:package:update shopping_feed_manager
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list
—
### β 3. Then check the logs:
tail -n 100 application/files/logs/*.log
—
## π Based on logs, hereβs what to do:
| Log Output Found | Action |
|——————|——–|
| β
`π₯ on_start()…` AND `β
DummyCommand registered` | π It's working β now `dummy:test` will appear |
| β `π₯ on_start()…` missing | β `on_start()` isn't being run β your package may not be updated |
| β `β
DummyCommand registered` missing | β οΈ Command registration failed β error message in log will say why |
—
## π If `on_start()` isnβt running at all:
Run this **to force re-initialize the package**:
./concrete/bin/concrete c5:package:update shopping_feed_manager
Then:
./concrete/bin/concrete list
—
Once you confirm `π₯ on_start()` log appears, the rest will work. Let me know the **latest logs**, and Iβll confirm the exact fix or next step.
> β The command class is **still not being autoloaded properly** despite `composer dump-autoload`.
Letβs **confirm and fix that now**.
—
## β Fix Autoloading + Command Instantiation
### β 1. Confirm file structure and namespace match
Check:
File path:
packages/shopping_feed_manager/src/Command/DummyCommand.php
Namespace inside the file:
namespace ConcretePackageShoppingFeedManagerCommand;
Class should begin like this:
<?php
namespace ConcretePackageShoppingFeedManagerCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class DummyCommand extends Command
{
protected function configure()
{
$this->setName('dummy:test');
$this->setDescription('Test dummy command');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('<info>Dummy command executed</info>');
return Command::SUCCESS;
}
}
—
### β 2. Update `controller.php` to reference class **by full namespace**
Replace your `$cli->add(…)` with:
$this->app->extend(ConcreteCoreConsoleApplication::class, function ($cli) {
Log::info("βοΈ Inside ConsoleApplication extender");
try {
$command = new ConcretePackageShoppingFeedManagerCommandDummyCommand();
$cli->add($command);
Log::info("β
DummyCommand registered successfully");
} catch (Throwable $e) {
Log::error("β DummyCommand registration failed: " . $e->getMessage());
}
return $cli;
});
—
### β 3. From project root, rebuild everything
composer dump-autoload
./concrete/bin/concrete c5:package:update shopping_feed_manager
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list | grep dummy
—
### β 4. Check logs again
tail -n 100 application/files/logs/*.log
> β If log says **Class not found**, autoloading is still broken.
> β
If log says **DummyCommand registered successfully**, the command **must show up**.
—
## β If it still fails
Add this quick sanity test in your `on_start()`:
if (class_exists(ConcretePackageShoppingFeedManagerCommandDummyCommand::class)) {
Log::info("β
DummyCommand class is found via class_exists");
} else {
Log::error("β DummyCommand class not found via class_exists");
}
That will definitively tell us if autoloading works.
—
Let me know the latest `logs/*.log` result. Once `DummyCommand` shows up, `GenerateFeedCommand` will too β exact same process.
new ConcretePackageShoppingFeedManagerCommandDummyCommand()
and the command **still doesnβt show**, it **confirms one thing**:
> π¨ Your command class isn't being autoloaded despite correct namespace and dump-autoload.
—
## β Fix This for Good: Manual `require_once` (Prove Autoloading Issue)
Letβs **bypass autoloading** for now to *prove the root issue*.
In your `controller.php`, just above the `new DummyCommand()` line:
require_once __DIR__ . '/src/Command/DummyCommand.php';
$this->app->extend(ConcreteCoreConsoleApplication::class, function ($cli) {
Log::info("βοΈ Manual load of DummyCommand.php");
try {
$command = new ConcretePackageShoppingFeedManagerCommandDummyCommand();
$cli->add($command);
Log::info("β
DummyCommand registered manually");
} catch (Throwable $e) {
Log::error("β Manual DummyCommand registration failed: " . $e->getMessage());
}
return $cli;
});
Then run:
./concrete/bin/concrete c5:clear-cache
./concrete/bin/concrete list | grep dummy
—
### β If this works (command shows up):
π― Youβve **proven the namespace is correct**, but **autoloading is NOT working**.
> β At this point, weβll **fix autoload permanently** with one of two options below.
—
## π Fix Autoloading Permanently (Once Proven)
### Option 1: Register your package inside main `/composer.json` (you likely already tried this)
"autoload": {
"psr-4": {
"Concrete\Package\ShoppingFeedManager\": "packages/shopping_feed_manager/src/"
}
}
Then from `/var/www/html`:
composer dump-autoload
—
### Option 2: Add `composer.json` directly inside your package
Create this file:
**`packages/shopping_feed_manager/composer.json`**
{
"autoload": {
"psr-4": {
"Concrete\Package\ShoppingFeedManager\": "src/"
}
}
}
Then run:
cd packages/shopping_feed_manager
composer dump-autoload
And in your main project, include this via the `wikimedia/composer-merge-plugin` if needed (some setups require it).
—
Let me know:
– Did `require_once` work and allow the command to appear?
– Do you want me to zip a **fully working test package** for reference?
Once the command appears via `require_once`, weβll patch autoloading once and for all.