1. Drupal console简述
drupal console是一个能够生成代码样本与drupal交互并提供命令行接口CLI操作的工具。从重建缓存,到列出路由、服务以及模块。甚至还可以和配置管理进行交互。
2. Drupal console的诞生
一切都始于使Drupal 8更好的想法,每一项伟大的发明/创新都源于一个构想,而Drupal从7过渡到8伴随着对昨天基本操作程序的巨大改变。Jesus和David是Drupal Console项目的发起人,提出了将symfony控制台纳入Drupal核心的想法。将其他symfony组件包含到Drupal核心当中。但最初并没有被Drupal社区广泛接受,接受新的理念通常需要一段时间的市场证明,在几次尝试要求纳入并尝试合作进行不同的drupal项目之后,Jesus和David突然意识到,融合和协作将不会发生。于是在哥斯达黎加Drupal camp午餐时,Jesus和David随便讨论了他们试图将创新引入Drupal和相关项目时遇到的挫败感,而Larry Garfield则吹嘘说“有人需要创建一个包含Symfony Console和代码生成的单独项目” 。这句话催生了今天的Drupal Console项目。
3. Drupal console与Drush的区别
drupal console是面向oop 设计即drupal8含版本以上的模块,而drush除了向下兼容之外,drush9也基于Symfony和POO思想重新设计开发的。实际上这两者区别不大,接下来从源码上解析一下两者:
- // Set up environment
- $environment = new Environment(Path::getHomeDirectory(), $cwd, $autoloadFile);
- $environment->setConfigFileVariant(Drush::getMajorVersion());
- $environment->setLoader($loader);
- $environment->applyEnvironment();
-
- // Preflight and run
- $preflight = new Preflight($environment);
- $di = new DependencyInjection();
- $di->desiredHandlers(['errorHandler', 'shutdownHandler']);
- $runtime = new Runtime($preflight, $di);
- $status_code = $runtime->run($_SERVER['argv']);
- 首先是加载当前的站点环境变量
- 把当前环境变量与命令行输入和注入处理器放到当前容器上下文当中
- protected function addDrushServices(ContainerInterface $container, ClassLoader $loader, DrupalFinder $drupalFinder, SiteAliasManager $aliasManager, DrushConfig $config) {
-
- // Override Robo's logger with our own
- $container->share('logger', 'Drush\Log\Logger')
- ->withArgument('output')
- ->withMethodCall('setLogOutputStyler', ['logStyler']);
-
- $container->share('loader', $loader);
- $container->share('site.alias.manager', $aliasManager);
-
- // Fetch the runtime config, where -D et. al. are stored, and
- // add a reference to it to the container.
- $container->share('config.runtime', $config->getContext(ConfigOverlay::PROCESS_CONTEXT));
-
- // Override Robo's formatter manager with our own
- // @todo not sure that we'll use this. Maybe remove it.
- $container->share('formatterManager', \Drush\Formatters\DrushFormatterManager::class)
- ->withMethodCall('addDefaultFormatters', [])
- ->withMethodCall('addDefaultSimplifiers', []);
-
- // Add some of our own objects to the container
- $container->share('bootstrap.drupal8', 'Drush\Boot\DrupalBoot8');
- $container->share('bootstrap.manager', 'Drush\Boot\BootstrapManager')
- ->withMethodCall('setDrupalFinder', [$drupalFinder]);
- // TODO: Can we somehow add these via discovery (e.g. backdrop extension?)
- $container->extend('bootstrap.manager')
- ->withMethodCall('add', ['bootstrap.drupal8']);
- $container->share('bootstrap.hook', 'Drush\Boot\BootstrapHook')
- ->withArgument('bootstrap.manager');
- $container->share('tildeExpansion.hook', 'Drush\Runtime\TildeExpansionHook');
- $container->share('process.manager', ProcessManager::class)
- ->withMethodCall('setConfig', ['config'])
- ->withMethodCall('setConfigRuntime', ['config.runtime']);
- $container->share('redispatch.hook', 'Drush\Runtime\RedispatchHook')
- ->withArgument('process.manager');
-
- // Robo does not manage the command discovery object in the container,
- // but we will register and configure one for our use.
- // TODO: Some old adapter code uses this, but the Symfony dispatcher does not.
- // See Application::commandDiscovery().
- $container->share('commandDiscovery', 'Consolidation\AnnotatedCommand\CommandFileDiscovery')
- ->withMethodCall('addSearchLocation', ['CommandFiles'])
- ->withMethodCall('setSearchPattern', ['#.*(Commands|CommandFile).php$#']);
-
- // Error and Shutdown handlers
- $container->share('errorHandler', 'Drush\Runtime\ErrorHandler');
- $container->share('shutdownHandler', 'Drush\Runtime\ShutdownHandler');
-
- // Add inflectors. @see \Drush\Boot\BaseBoot::inflect
- $container->inflector(\Drush\Boot\AutoloaderAwareInterface::class)
- ->invokeMethod('setAutoloader', ['loader']);
- $container->inflector(\Consolidation\SiteAlias\SiteAliasManagerAwareInterface::class)
- ->invokeMethod('setSiteAliasManager', ['site.alias.manager']);
- $container->inflector(\Consolidation\SiteProcess\ProcessManagerAwareInterface::class)
- ->invokeMethod('setProcessManager', ['process.manager']);}
- 之后获取容器runtime的上下文放入logger接收输出,实现ProcessManagerAwareInterface接口调起一个子进程反射执行方法
- protected function injectApplicationServices(ContainerInterface $container, Application $application)
- {
- $application->setLogger($container->get('logger'));
- $application->setBootstrapManager($container->get('bootstrap.manager'));
- $application->setAliasManager($container->get('site.alias.manager'));
- $application->setRedispatchHook($container->get('redispatch.hook'));
- $application->setTildeExpansionHook($container->get('tildeExpansion.hook'));
- $application->setDispatcher($container->get('eventDispatcher'));
- $application->setConfig($container->get('config')); }
- 最后将一系列的模组注入到当前的ApplicationServices里面通过Symfony Console component运行命令
- $output = new ConsoleOutput();
- $input = new ArrayInput([]);
- $io = new DrupalStyle($input, $output);
- $argvInputReader = new ArgvInputReader();
- $root = $argvInputReader->get('root', getcwd());
-
- $drupalFinder = new DrupalFinder();
- if (!$drupalFinder->locateRoot($root)) {
- $io->error('DrupalConsole must be executed within a Drupal Site.');
-
- exit(1);
- }
-
- chdir($drupalFinder->getDrupalRoot());
- $configurationManager = new ConfigurationManager();
- $configuration = $configurationManager
- ->loadConfiguration($drupalFinder->getComposerRoot())
- ->getConfiguration();
- $debug = $argvInputReader->get('debug', false);
- if ($configuration && $options = $configuration->get('application.options') ?: []) {
- $argvInputReader->setOptionsFromConfiguration($options);
- }
- $argvInputReader->setOptionsAsArgv();
- if ($debug) {
- $io->writeln(
- sprintf(
- '<info>%s</info> version <comment>%s</comment>',
- Application::NAME,
- Application::VERSION
- )
- );
- }
- $drupal = new Drupal($autoload, $drupalFinder, $configurationManager);
- $container = $drupal->boot();
- if (!$container) {
- $io->error('Something was wrong. Drupal can not be bootstrap.');
-
- exit(1);
- }
- $application = new Application($container);
- $application->setDrupal($drupal);
- $application->run();
- public function run(InputInterface $input = null, OutputInterface $output = null)
- {
- putenv('LINES='.$this->terminal->getHeight());
- putenv('COLUMNS='.$this->terminal->getWidth());
- if (null === $input) {
- $input = new ArgvInput();
- }
- if (null === $output) {
- $output = new ConsoleOutput();
- }
- $renderException = function ($e) use ($output) {
- if (!$e instanceof \Exception) {
- $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
- }
- if ($output instanceof ConsoleOutputInterface) {
- $this->renderException($e, $output->getErrorOutput());
- } else {
- $this->renderException($e, $output);
- }
- };
- if ($phpHandler = set_exception_handler($renderException)) {
- restore_exception_handler();
- if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
- $debugHandler = true;
- } elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
- $phpHandler[0]->setExceptionHandler($debugHandler);
- }
- }
-
- if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) {
- @trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED);
- }
-
- $this->configureIO($input, $output);
- try {
- $exitCode = $this->doRun($input, $output);
- } catch (\Exception $e) {
- if (!$this->catchExceptions) {
- throw $e;
- }
- $renderException($e);
- $exitCode = $e->getCode();
- if (is_numeric($exitCode)) {
- $exitCode = (int) $exitCode;
- if (0 === $exitCode) {
- $exitCode = 1;
- }
- } else {
- $exitCode = 1;
- }
- } finally {
- // if the exception handler changed, keep it
- // otherwise, unregister $renderException
- if (!$phpHandler) {
- if (set_exception_handler($renderException) === $renderException) {
- restore_exception_handler();
- }
- restore_exception_handler();
- } elseif (!$debugHandler) {
- $finalHandler = $phpHandler[0]->setExceptionHandler(null);
- if ($finalHandler !== $renderException) {
- $phpHandler[0]->setExceptionHandler($finalHandler);
- }
- }
- }
- if ($this->autoExit) {
- if ($exitCode > 255) {
- $exitCode = 255;
- }
- exit($exitCode);
- }
- return $exitCode;
- }
Drupal console 加载过程更为简洁,直接加载环境配置和drupal的bootstrap kernel然后全部交给Symfony Console component运行命令。
在9版本之前drush跟drupal console相比,确实没有代码生成器功能,实际上drush 在9版本开始就已经拥有跟drupal console同样的功能了即代码生成器,在创建模块的时候会有一些细微的交互区别,例如drush会询问要创建library.yml,Permissions.yml,block,controller等。
- Drupal console与Drush的安装方法
- Drupal console:
- Curl
curl https://drupalconsole.com/installer -L -o drupal.phar
然后mv drupal.phar /usr/bin/drupal;chmod +x /usr/bin/drupal 变成全局变量
- php 内置库请求安装
php -r "readfile('https://drupalconsole.com/installer');" > drupal.phar
然后mv drupal.phar /usr/bin/drupal;chmod +x /usr/bin/drupal 变成全局变量
最后去到网站根目录执行 drupal site:status 或者用别名drupal ss就能验证是否安装好了
- Composer为了不污染环境没有使用全局,容器内安装,不用global
composer require drupal/console:@stable
当前用户变量别名添加~/.bashrc
alias drupal=’<网站根目录>/vendor/drupal/console/bin/’
然后就可以用了例如生成reset api模块
drupal gprr
- Drush
根据drush官方文档安装对应兼容drupal的版本
Drush Version | Drush Branch | PHP | Compatible Drupal versions | Code Status |
Drush 9 | master | 5.5+ | D7, D8 | |
Drush 8 | 8.x | 5.4.5+ | D6, D7, D8 | |
Drush 7 | 7.x | 5.3.0+ | D6, D7 | |
Drush 6 | 6.x | 5.3.0+ | D6, D7 | Unsupported |
Drush 5 | 5.x | 5.2.0+ | D6, D7 | Unsupported |
Drush 4 | 4.x | 5.2.0+ | D5, D6, D7 | Unsupported |
Drush 3 | 3.x | 5.2.0+ | D5, D6 | Unsupported |
例如我用D8版本
- Composer 同理为了不污染环境,容器内安装,不用global
composer require drush/drush:dev-master
当前用户变量别名添加~/.bashrc
alias drupal=’<网站根目录>vendor/drush/drush/drush’
然后就可以用了例如drush cr 清缓存
2.源码包安装
git clone https://github.com/drush-ops/drush/releases,找到drush.phar
mv drush.phar drush && chmod +x drush;mv drush /usr/bin/;drush init
接下来初始化一路看到ok让你选择安装目录y后都成功的话那就是安装成功了
#常用操作命令只能在<网站根目录>下使用
- Drupal console
- 维护模式开/关
drupal site:maintenance on/off
3.开发/生产模式切换
drupal site:mode dev/prod
4.创建Node/Users/Terms测试数据
drupal create:nodes
drupal create:users
drupal create:terms
5.创建模块
- drupal generate:modulename
- // Welcome to the Drupal module generator
- Enter the new module name:
- > hello
- Enter the module machine name [hello]:
- >
- Enter the module Path [/modules/custom]:
- >
- Enter module description [My Awesome Module]:
- > ttt.
- Enter package name [Custom]:
- > test
- Enter Drupal Core version [8.x]:
- >
- Do you want to generate a .module file (yes/no) [yes]:
- >
- Define module as feature (yes/no) [no]:
- >
- Do you want to add a composer.json file to your module (yes/no) [yes]:
- >
- Would you like to add module dependencies (yes/no) [no]:
- >
- Do you want to generate a unit test class (yes/no) [yes]:
- >
- Do you want to generate a themeable template (yes/no) [yes]:
- >
- Do you confirm generation? (yes/no) [yes]:
- >
- Generated or updated files
- 1 - /var/www/html/drupal8/web/modules/custom/hello/hello.info.yml
- 2 - /var/www/html/drupal8/web/modules/custom/hello/hello.module
- 3 - /var/www/html/drupal8/web/modules/custom/hello/composer.json
- 4 - /var/www/html/drupal8/web/modules/custom/hello/tests/src/Functional/LoadTest.php
- 5 - /var/www/html/drupal8/web/modules/custom/hello/hello.module
- 6 - /var/www/html/drupal8/web/modules/custom/hello/templates/hello.html.twig
写完模块当然是安装了
drupal module:install hello
上面只有sample code 想要渲染一些东西到页面还得生成一个controller
- $ drupal generate:controller
- // Welcome to the Drupal Controller generator
- Enter the module name [hello]:
- >
- Enter the Controller class name [DefaultController]:
- >
- Enter the Controller method title (to stop adding more methods, leave this empty) [ ]:
- > Hello Awesome
- Enter the action method name [hello]:
- > hello
- Enter the route path [/hello/Hello]:
- > /hello/example
- Enter the Controller method title (to stop adding more methods, leave this empty) [ ]:
- >
- Do you want to generate a unit test class (yes/no) [yes]:
- >
- Do you want to load services from the container (yes/no) [no]:
- >
- Do you confirm generation? (yes/no) [yes]:
- >
之后访问http://localhost/hello/example,就可以看到页面了
- 创建一个block layout
- $ drupal generate:plugin:block
-
- // Welcome to the Drupal Plugin Block generator
- Enter the module name [hellosanta]:
- > hello
- Enter the plugin class name [DefaultBlock]:
- > testblock
- Enter the plugin label [testblock]:
- >
- Enter the plugin id [testblock]:
- > hello-block-1
- Enter the theme region to render the Plugin Block. [ ]:
- >
- Do you want to load services from the container (yes/no) [no]:
- >
- You can add input fields to create special configurations in the block.
- This is optional, press enter to continue
- Do you want to generate a form structure? (yes/no) [yes]:
- >
- New field type (press <return> to stop adding fields) [ ]:
- > textfield
- Input label:
- > Name
- Input machine name [name]:
- >
- Maximum amount of characters [64]:
- > 10
- Width of the textfield (in characters) [64]:
- > 30
- Description [ ]:
- > Please leave your name
- Default value [ ]:
- > Test
- Weight for input item [0]:
- >
- New field type (press <return> to stop adding fields) [ ]:
- > email
- Input label:
- > Email
- Input machine name [email]:
- >
- Description [ ]:
- > Please leave your Email
- Default value [ ]:
- > jin@126.com
- Weight for input item [0]:
- > 1
- New field type (press <return> to stop adding fields) [ ]:
- >
- Do you confirm generation? (yes/no) [yes]:
- >
默认region是放在content里的所以只要访问上一步的url就能看见这个block了
- Drush
- 改账号密码
drush upwd admin 123456
- 清除超过5次登陆错误锁定账号
drush php-eval 'db_query("DELETE FROM `flood`");'
drush user-unblock <user>
- 清缓存
选择类型缓存drush cc
Enter a number to choose which cache to clear.
[0] : Cancel
[1] : all
[2] : drush
[3] : theme-registry
[4] : menu
[5] : css-js
[6] : block
[7] : module-list
[8] : theme-list
[9] : registry
[10] : token
[11] : views
全局类型清缓存drush cr
- 模块下载/启用/更新
下载:drush en 模块名 -y
启用:drush en admin-menu -y
更新:drush upc
刷新数据库:drush updatedb
2.数据库备份
drush cr all && drush sql-dump --result-file=../19.sql 或者压缩 drush sql-dump --gzip --result-file=../19.sql