Based on our exchange in the comments and the question, you're going to need at least 3 applications. I'll give you a quick examples, you'll have to update your requirements for each application yourself. After the composer.json configs I'll give you a skeleton module to use as a theme module.
These config's are to be used as the root
composer.json config files. Each of the required packages should have their own composer file listing requirements for the specific package.
For example, a "core" module would require various Zend Framework packages. A "theme" package could be requiring other ZF packages, such as zendframework/zend-view
in order to be able to have a GUI layout.
Setting up 3 separate Zend Framework applications with overlapping requirements
composer.json
for application 1
{
"name": "COMPANY_NAME/APPLICATION_1",
"require": {
"COMPANY_NAME/MODULE_1_THEME": "*",
"COMPANY_NAME/MODULE_2_CMS": "*"
},
"repositories": [
{
"type": "git",
"url": "git@github.com/COMPANY_NAME/MODULE_1_THEME.git"
},
{
"type": "git",
"url": "git@github.com/COMPANY_NAME/MODULE_2_CMS.git"
},
]
}
composer.json
for application 2
{
"name": "COMPANY_NAME/APPLICATION_2",
"require": {
"COMPANY_NAME/MODULE_1_THEME": "*",
"COMPANY_NAME/MODULE_3_ACCOUNTING": "*"
},
"repositories": [
{
"type": "git",
"url": "git@github.com/COMPANY_NAME/MODULE_1_THEME.git"
},
{
"type": "git",
"url": "git@github.com/COMPANY_NAME/MODULE_3_ACCOUNTING.git"
},
]
}
composer.json
for application 3 (has no theme)
{
"name": "COMPANY_NAME/APPLICATION_3",
"require": {
"COMPANY_NAME/MODULE_4_AUTH_MODULE": "*"
},
"repositories": [
{
"type": "git",
"url": "git@github.com/COMPANY_NAME/MODULE_4_AUTH_MODULE.git"
}
]
}
As you can see, the Applications 1 & 2 use the same MODULE_THEME
package, as you outlined in the diagram in your question.
Now, the creation of a package for Zend Framework is pretty much the same for every package you create, so modify what follows to the requirements you have for each module (in a package).
Creating a theme module
This module basically replaces the Application
module that you get by default when you install the Zend Framework (2 or 3) Skeleton Application.
I've recently upgraded everything I have with Zend Framework to Zend Framework 3, so I'll be giving you a setup tailored for ZF3. However, downgrading for ZF2 should not be too much of an issue.
Create config for what you need
A typical theme needs a few things, such as:
- themes/layouts for different types of pages (e.g. login, normal theme, errors)
- translations
- showing errors (when in dev mode)
- default "home" route
- controller to handle default "home" route
Config for this could be (not limited to! Do with it what you wish!) as such in the module.config.php
of the Theme module
namespace COMPANY_NAME\Theme;
use COMPANY_NAME\Theme\Controller\ThemeController;
use COMPANY_NAME\Theme\Factory\ThemeControllerFactory;
return [
'controllers' => [
'factories' => [
ThemeController::class => ThemeControllerFactory::class,
],
],
'router' => [
'routes' => [
'home' => [
'type' => Literal::class,
'may_terminate' => true,
'options' => [
'route' => '/',
'defaults' => [
'controller' => ThemeController::class,
'action' => 'index',
],
],
],
],
],
'route_layouts' => [
'*' => 'layout/layout',
'login' => 'layout/login',
'register' => 'layout/login',
'error*' => 'error/index',
'error/404' => 'error/404',
],
'translator' => [
'locale' => 'en_US',
'translation_file_patterns' => [
[
'type' => 'gettext',
'base_dir' => __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'language',
'pattern' => '%s.mo',
],
],
],
'view_manager' => [
// controller_map is optional, but depending on your composer package nesting, could be a great help. Have a look here for how to use: https://blog.alejandrocelaya.com/2015/08/14/working-with-sub-namespaced-modules-in-zend-framework-2-the-right-way/
'controller_map' => [
'COMPANY_NAME\Theme' => 'company_name_path_alias',
],
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => [
'layout/layout' => __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR .
'layout' . DIRECTORY_SEPARATOR . 'layout.phtml',
'layout/login' => __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR .
'layout' . DIRECTORY_SEPARATOR . 'login.phtml',
'error/404' => __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR .
'error' . DIRECTORY_SEPARATOR . '404.phtml',
'error/index' => __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR .
'error' . DIRECTORY_SEPARATOR . 'index.phtml',
],
'template_path_stack' => [
__DIR__ . DIRECTORY_SEPARATOR .'..' . DIRECTORY_SEPARATOR . 'view',
],
],
];
File/module structure based on config
The location of the package would be /vendor/COMPANY_NAME/THEME_MODULE_NAME
(as you would've defined in the name
property in the composer.json
file for this package.
The folder/file structure would be:
- /vendor/COMPANY_NAME/THEME_MODULE_NAME
- config/
- src/
- Controller/
- Factory/
- ThemeControllerFactory.php
- Module.php
- view/
- error/
- layout/
- index.phtml
- login.phtml
- register.phtml
- composer.json
ThemeController & *Factory
These are very simple as the Controller is pretty much a clone of the original IndexController
provided by the Skeleton Application. The Factory in this instance does nothing but return the Controller. As such you could replace the config for it with the FQCN to the InvokableFactory
of Zend Framework 3 and not make the Factory class. However, if your ThemeController
needs some requirements (such as a RegisterForm
), you're going to need the Factory to provide these.
ThemeController
namespace COMPANY_NAME\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class ThemeController extends AbstractActionController
{
public function indexAction()
{
return [];
}
}
ThemeControllerFactory
namespace COMPANY_NAME\Factory;
use COMPANY_NAME\Controller\ThemeController;
use Zend\ServiceManager\Factory\FactoryInterface;
class ThemeControllerFactory implements FactoryInterface
{
/**
* @param ContainerInterface $container
* @param string $requestedName
* @param array|null $options
* @return ThemeController
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new ThemeController();
}
}
Theme composer requirements
Obviously your modules will not have the same requirements. Make sure you figure out what they are, per module.
For my own Theme module, I have the following Zend Framework requirements in my composer.json
file:
{
"name": "COMPANY_NAME/THEME_MODULE_NAME",
"require": {
"zendframework/zend-di": "*",
"zendframework/zend-navigation": "*",
"zendframework/zend-view": "*",
}
}
In the require
section I also have: "rwoverdijk/assetmanager": "^1.6",
. This module is used to mash together all CSS, JS (any type really) of file to a determined location. I would advise you to have a look at it (here).
Notes on the answer
- Replace
COMPANY_NAME
with the username of your Github account (or the identifying account name if your using Bitbucket or Gitlab)
- Replace
THEME_MODULE_NAME
with the name of the repository
- If/when possible, use explicit versions for required packages (e.g.
"rwoverdijk/assetmanager": "^1.6"
). Version locking can save you a lot of hassle in the future...
Additionally: using a package as a "Theme module" allows you to completely remove the module/
folder originally shipped with the Skeleton Application of Zend Framework. However, you're hereby advised to use the module/
folder for application specific modules. If you create a package for everything, you'll soon find yourself maintenance hell.