Initial Drupal 11 with DDEV setup
This commit is contained in:
		@ -0,0 +1,301 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite\Commands;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Config\ConfigImporter;
 | 
			
		||||
use Drupal\Core\Database\Database;
 | 
			
		||||
use Drupal\Core\Test\FunctionalTestSetupTrait;
 | 
			
		||||
use Drupal\Core\Test\TestDatabase;
 | 
			
		||||
use Drupal\Core\Test\TestSetupTrait;
 | 
			
		||||
use Drupal\TestSite\TestPreinstallInterface;
 | 
			
		||||
use Drupal\TestSite\TestSetupInterface;
 | 
			
		||||
use Drupal\Tests\RandomGeneratorTrait;
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Input\InputOption;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
use Symfony\Component\Console\Style\SymfonyStyle;
 | 
			
		||||
use Symfony\Component\Filesystem\Filesystem;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Command to create a test Drupal site.
 | 
			
		||||
 *
 | 
			
		||||
 * @internal
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteInstallCommand extends Command {
 | 
			
		||||
 | 
			
		||||
  use FunctionalTestSetupTrait {
 | 
			
		||||
    installParameters as protected installParametersTrait;
 | 
			
		||||
  }
 | 
			
		||||
  use RandomGeneratorTrait;
 | 
			
		||||
  use TestSetupTrait {
 | 
			
		||||
    changeDatabasePrefix as protected changeDatabasePrefixTrait;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The theme to install as the default for testing.
 | 
			
		||||
   *
 | 
			
		||||
   * Defaults to the install profile's default theme, if it specifies any.
 | 
			
		||||
   */
 | 
			
		||||
  protected string $defaultTheme;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The base URL.
 | 
			
		||||
   */
 | 
			
		||||
  protected string $baseUrl;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The original array of shutdown function callbacks.
 | 
			
		||||
   */
 | 
			
		||||
  protected array $originalShutdownCallbacks = [];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The config importer that can be used in a test.
 | 
			
		||||
   */
 | 
			
		||||
  protected ?ConfigImporter $configImporter;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The install profile to use.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $profile = 'testing';
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Time limit in seconds for the test.
 | 
			
		||||
   *
 | 
			
		||||
   * Used by \Drupal\Core\Test\FunctionalTestSetupTrait::prepareEnvironment().
 | 
			
		||||
   *
 | 
			
		||||
   * @var int
 | 
			
		||||
   */
 | 
			
		||||
  protected $timeLimit = 500;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The language to install the site in.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $langcode = 'en';
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function configure() {
 | 
			
		||||
    $this->setName('install')
 | 
			
		||||
      ->setDescription('Creates a test Drupal site')
 | 
			
		||||
      ->setHelp('The details to connect to the test site created will be displayed upon success. It will contain the database prefix and the user agent.')
 | 
			
		||||
      ->addOption('setup-file', NULL, InputOption::VALUE_OPTIONAL, 'The path to a PHP file containing a class to setup configuration used by the test, for example, core/tests/Drupal/TestSite/TestSiteMultilingualInstallTestScript.php.')
 | 
			
		||||
      ->addOption('db-url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database. Defaults to the environment variable SIMPLETEST_DB.', getenv('SIMPLETEST_DB'))
 | 
			
		||||
      ->addOption('base-url', NULL, InputOption::VALUE_OPTIONAL, 'Base URL for site under test. Defaults to the environment variable SIMPLETEST_BASE_URL.', getenv('SIMPLETEST_BASE_URL'))
 | 
			
		||||
      ->addOption('install-profile', NULL, InputOption::VALUE_OPTIONAL, 'Install profile to install the site in. Defaults to testing.', 'testing')
 | 
			
		||||
      ->addOption('langcode', NULL, InputOption::VALUE_OPTIONAL, 'The language to install the site in. Defaults to en.', 'en')
 | 
			
		||||
      ->addOption('json', NULL, InputOption::VALUE_NONE, 'Output test site connection details in JSON.')
 | 
			
		||||
      ->addUsage('--setup-file core/tests/Drupal/TestSite/TestSiteMultilingualInstallTestScript.php --json')
 | 
			
		||||
      ->addUsage('--install-profile demo_umami --langcode fr')
 | 
			
		||||
      ->addUsage('--base-url "http://example.com" --db-url "mysql://username:password@localhost/database_name#table_prefix"');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function execute(InputInterface $input, OutputInterface $output): int {
 | 
			
		||||
    // Determines and validates the setup class prior to installing a database
 | 
			
		||||
    // to avoid creating unnecessary sites.
 | 
			
		||||
    $root = dirname(__DIR__, 5);
 | 
			
		||||
    chdir($root);
 | 
			
		||||
    $class_name = $this->getSetupClass($input->getOption('setup-file'));
 | 
			
		||||
    // Ensure we can install a site in the sites/simpletest directory.
 | 
			
		||||
    $this->ensureDirectory($root);
 | 
			
		||||
 | 
			
		||||
    $db_url = $input->getOption('db-url');
 | 
			
		||||
    $base_url = $input->getOption('base-url');
 | 
			
		||||
    putenv("SIMPLETEST_DB=$db_url");
 | 
			
		||||
    putenv("SIMPLETEST_BASE_URL=$base_url");
 | 
			
		||||
 | 
			
		||||
    // Manage site fixture.
 | 
			
		||||
    $this->setup($input->getOption('install-profile'), $class_name, $input->getOption('langcode'));
 | 
			
		||||
 | 
			
		||||
    // Make sure there is an entry in sites.php for the new site.
 | 
			
		||||
    $fs = new Filesystem();
 | 
			
		||||
    if (!$fs->exists($root . '/sites/sites.php')) {
 | 
			
		||||
      $fs->copy($root . '/sites/example.sites.php', $root . '/sites/sites.php');
 | 
			
		||||
    }
 | 
			
		||||
    $parsed = parse_url($base_url);
 | 
			
		||||
    $port = $parsed['port'] ?? 80;
 | 
			
		||||
    $host = $parsed['host'] ?? 'localhost';
 | 
			
		||||
    // Remove 'sites/' from the beginning of the path.
 | 
			
		||||
    $site_path = substr($this->siteDirectory, 6);
 | 
			
		||||
    $fs->appendToFile($root . '/sites/sites.php', "\$sites['$port.$host'] = '$site_path';");
 | 
			
		||||
 | 
			
		||||
    $user_agent = drupal_generate_test_ua($this->databasePrefix);
 | 
			
		||||
    if ($input->getOption('json')) {
 | 
			
		||||
      $output->writeln(json_encode([
 | 
			
		||||
        'db_prefix' => $this->databasePrefix,
 | 
			
		||||
        'user_agent' => $user_agent,
 | 
			
		||||
        'site_path' => $this->siteDirectory,
 | 
			
		||||
      ]));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $output->writeln('<info>Successfully installed a test site</info>');
 | 
			
		||||
      $io = new SymfonyStyle($input, $output);
 | 
			
		||||
      $io->table([], [
 | 
			
		||||
        ['Database prefix', $this->databasePrefix],
 | 
			
		||||
        ['User agent', $user_agent],
 | 
			
		||||
        ['Site path', $this->siteDirectory],
 | 
			
		||||
      ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the setup class.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string|null $file
 | 
			
		||||
   *   The file to get the setup class from.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string|null
 | 
			
		||||
   *   The setup class contained in the provided $file.
 | 
			
		||||
   *
 | 
			
		||||
   * @throws \InvalidArgumentException
 | 
			
		||||
   *   Thrown if the file does not exist, does not contain a class or the class
 | 
			
		||||
   *   does not implement \Drupal\TestSite\TestSetupInterface or
 | 
			
		||||
   *   \Drupal\TestSite\TestPreinstallInterface.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getSetupClass($file) {
 | 
			
		||||
    if ($file === NULL) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (!file_exists($file)) {
 | 
			
		||||
      throw new \InvalidArgumentException("The file $file does not exist.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $classes = get_declared_classes();
 | 
			
		||||
    include_once $file;
 | 
			
		||||
    $new_classes = array_values(array_diff(get_declared_classes(), $classes));
 | 
			
		||||
    if (empty($new_classes)) {
 | 
			
		||||
      throw new \InvalidArgumentException("The file $file does not contain a class.");
 | 
			
		||||
    }
 | 
			
		||||
    $class = array_pop($new_classes);
 | 
			
		||||
 | 
			
		||||
    if (!is_subclass_of($class, TestSetupInterface::class) && !is_subclass_of($class, TestPreinstallInterface::class)) {
 | 
			
		||||
      throw new \InvalidArgumentException("The class $class contained in $file needs to implement \Drupal\TestSite\TestSetupInterface or \Drupal\TestSite\TestPreinstallInterface");
 | 
			
		||||
    }
 | 
			
		||||
    return $class;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Ensures that the sites/simpletest directory exists and is writable.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $root
 | 
			
		||||
   *   The Drupal root.
 | 
			
		||||
   */
 | 
			
		||||
  protected function ensureDirectory($root) {
 | 
			
		||||
    if (!is_writable($root . '/sites/simpletest')) {
 | 
			
		||||
      if (!@mkdir($root . '/sites/simpletest')) {
 | 
			
		||||
        throw new \RuntimeException($root . '/sites/simpletest must exist and be writable to install a test site');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a test drupal installation.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $profile
 | 
			
		||||
   *   (optional) The installation profile to use.
 | 
			
		||||
   * @param string $setup_class
 | 
			
		||||
   *   (optional) Setup class. A PHP class to setup configuration used by the
 | 
			
		||||
   *   test.
 | 
			
		||||
   * @param string $langcode
 | 
			
		||||
   *   (optional) The language to install the site in.
 | 
			
		||||
   */
 | 
			
		||||
  public function setup($profile = 'testing', $setup_class = NULL, $langcode = 'en') {
 | 
			
		||||
    $this->profile = $profile;
 | 
			
		||||
    $this->langcode = $langcode;
 | 
			
		||||
    $this->setupBaseUrl();
 | 
			
		||||
    $this->prepareEnvironment();
 | 
			
		||||
    $this->executePreinstallClass($setup_class);
 | 
			
		||||
    $this->installDrupal();
 | 
			
		||||
    $this->executeSetupClass($setup_class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Installs Drupal into the test site.
 | 
			
		||||
   */
 | 
			
		||||
  protected function installDrupal() {
 | 
			
		||||
    $this->initUserSession();
 | 
			
		||||
    $this->prepareSettings();
 | 
			
		||||
    $this->doInstall();
 | 
			
		||||
    $this->initSettings();
 | 
			
		||||
    $container = $this->initKernel(\Drupal::request());
 | 
			
		||||
    $this->initConfig($container);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Uses the setup file to configure Drupal.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $class
 | 
			
		||||
   *   The fully qualified class name, which should set up Drupal for tests. For
 | 
			
		||||
   *   example this class could create content types and fields or install
 | 
			
		||||
   *   modules. The class needs to implement TestSetupInterface.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\TestSite\TestSetupInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected function executeSetupClass($class) {
 | 
			
		||||
    if (is_subclass_of($class, TestSetupInterface::class)) {
 | 
			
		||||
      /** @var \Drupal\TestSite\TestSetupInterface $instance */
 | 
			
		||||
      $instance = new $class();
 | 
			
		||||
      $instance->setup();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Uses the setup file to configure the environment prior to install.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $class
 | 
			
		||||
   *   The fully qualified class name, which should set up the environment prior
 | 
			
		||||
   *   to installing Drupal for tests. For example this class could create
 | 
			
		||||
   *   translations that are used during the installer.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\TestSite\TestPreinstallInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected function executePreinstallClass($class) {
 | 
			
		||||
    if (is_subclass_of($class, TestPreinstallInterface::class)) {
 | 
			
		||||
      /** @var \Drupal\TestSite\TestPreinstallInterface $instance */
 | 
			
		||||
      $instance = new $class();
 | 
			
		||||
      $instance->preinstall($this->databasePrefix, $this->siteDirectory);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function installParameters() {
 | 
			
		||||
    $parameters = $this->installParametersTrait();
 | 
			
		||||
    $parameters['parameters']['langcode'] = $this->langcode;
 | 
			
		||||
    return $parameters;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function changeDatabasePrefix() {
 | 
			
		||||
    // Ensure that we use the database from SIMPLETEST_DB environment variable.
 | 
			
		||||
    Database::removeConnection('default');
 | 
			
		||||
    $this->changeDatabasePrefixTrait();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function prepareDatabasePrefix() {
 | 
			
		||||
    // Override this method so that we can force a lock to be created.
 | 
			
		||||
    $test_db = new TestDatabase(NULL, TRUE);
 | 
			
		||||
    $this->siteDirectory = $test_db->getTestSitePath();
 | 
			
		||||
    $this->databasePrefix = $test_db->getDatabasePrefix();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,42 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite\Commands;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Test\TestDatabase;
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Command to release all test site database prefix locks.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that this command can't be safely tested by DrupalCI without potentially
 | 
			
		||||
 * causing random failures.
 | 
			
		||||
 *
 | 
			
		||||
 * @internal
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteReleaseLocksCommand extends Command {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function configure() {
 | 
			
		||||
    $this->setName('release-locks')
 | 
			
		||||
      ->setDescription('Releases all test site locks')
 | 
			
		||||
      ->setHelp('The locks ensure test site database prefixes are not reused.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function execute(InputInterface $input, OutputInterface $output): int {
 | 
			
		||||
    $root = dirname(__DIR__, 5);
 | 
			
		||||
    chdir($root);
 | 
			
		||||
    TestDatabase::releaseAllTestLocks();
 | 
			
		||||
    $output->writeln('<info>Successfully released all the test database locks</info>');
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,139 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite\Commands;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Database\Database;
 | 
			
		||||
use Drupal\Core\Test\TestDatabase;
 | 
			
		||||
use Drupal\Tests\BrowserTestBase;
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Input\InputArgument;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Input\InputOption;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
use Symfony\Component\Console\Style\SymfonyStyle;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Command to tear down a test Drupal site.
 | 
			
		||||
 *
 | 
			
		||||
 * @internal
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteTearDownCommand extends Command {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function configure() {
 | 
			
		||||
    $this->setName('tear-down')
 | 
			
		||||
      ->setDescription('Removes a test site added by the install command')
 | 
			
		||||
      ->setHelp('All the database tables and files will be removed.')
 | 
			
		||||
      ->addArgument('db-prefix', InputArgument::REQUIRED, 'The database prefix for the test site.')
 | 
			
		||||
      ->addOption('db-url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database. Defaults to the environment variable SIMPLETEST_DB.', getenv('SIMPLETEST_DB'))
 | 
			
		||||
      ->addOption('keep-lock', NULL, InputOption::VALUE_NONE, 'Keeps the database prefix lock. Useful for ensuring test isolation when running concurrent tests.')
 | 
			
		||||
      ->addUsage('test12345678')
 | 
			
		||||
      ->addUsage('test12345678 --db-url "mysql://username:password@localhost/database_name#table_prefix"')
 | 
			
		||||
      ->addUsage('test12345678 --keep-lock');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function execute(InputInterface $input, OutputInterface $output): int {
 | 
			
		||||
    $root = dirname(__DIR__, 5);
 | 
			
		||||
    chdir($root);
 | 
			
		||||
    $db_prefix = $input->getArgument('db-prefix');
 | 
			
		||||
    // Validate the db_prefix argument.
 | 
			
		||||
    try {
 | 
			
		||||
      $test_database = new TestDatabase($db_prefix);
 | 
			
		||||
    }
 | 
			
		||||
    catch (\InvalidArgumentException) {
 | 
			
		||||
      $io = new SymfonyStyle($input, $output);
 | 
			
		||||
      $io->getErrorStyle()->error("Invalid database prefix: $db_prefix\n\nValid database prefixes match the regular expression '/test(\d+)$/'. For example, 'test12345678'.");
 | 
			
		||||
      // Display the synopsis of the command like Composer does.
 | 
			
		||||
      $output->writeln(sprintf('<info>%s</info>', sprintf($this->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
 | 
			
		||||
      return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $db_url = $input->getOption('db-url');
 | 
			
		||||
    putenv("SIMPLETEST_DB=$db_url");
 | 
			
		||||
 | 
			
		||||
    // Handle the cleanup of the test site.
 | 
			
		||||
    $this->tearDown($test_database, $db_url);
 | 
			
		||||
 | 
			
		||||
    // Release the test database prefix lock.
 | 
			
		||||
    if (!$input->getOption('keep-lock')) {
 | 
			
		||||
      $test_database->releaseLock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $output->writeln("<info>Successfully uninstalled $db_prefix test site</info>");
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Removes a given instance by deleting all the database tables and files.
 | 
			
		||||
   *
 | 
			
		||||
   * @param \Drupal\Core\Test\TestDatabase $test_database
 | 
			
		||||
   *   The test database object.
 | 
			
		||||
   * @param string $db_url
 | 
			
		||||
   *   The database URL.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\Tests\BrowserTestBase::cleanupEnvironment()
 | 
			
		||||
   */
 | 
			
		||||
  protected function tearDown(TestDatabase $test_database, $db_url): void {
 | 
			
		||||
    // Connect to the test database.
 | 
			
		||||
    $root = dirname(__DIR__, 5);
 | 
			
		||||
    $database = Database::convertDbUrlToConnectionInfo($db_url, $root);
 | 
			
		||||
    $database['prefix'] = $test_database->getDatabasePrefix();
 | 
			
		||||
    Database::addConnectionInfo(__CLASS__, 'default', $database);
 | 
			
		||||
 | 
			
		||||
    // Remove all the tables.
 | 
			
		||||
    $schema = Database::getConnection('default', __CLASS__)->schema();
 | 
			
		||||
    $tables = $schema->findTables('%');
 | 
			
		||||
    array_walk($tables, [$schema, 'dropTable']);
 | 
			
		||||
 | 
			
		||||
    // Delete test site directory.
 | 
			
		||||
    $this->fileUnmanagedDeleteRecursive($root . DIRECTORY_SEPARATOR . $test_database->getTestSitePath(), [BrowserTestBase::class, 'filePreDeleteCallback']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Deletes all files and directories in the specified path recursively.
 | 
			
		||||
   *
 | 
			
		||||
   * Note this method has no dependencies on Drupal core to ensure that the
 | 
			
		||||
   * test site can be torn down even if something in the test site is broken.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $path
 | 
			
		||||
   *   A string containing either a URI or a file or directory path.
 | 
			
		||||
   * @param callable $callback
 | 
			
		||||
   *   (optional) Callback function to run on each file prior to deleting it and
 | 
			
		||||
   *   on each directory prior to traversing it. For example, can be used to
 | 
			
		||||
   *   modify permissions.
 | 
			
		||||
   *
 | 
			
		||||
   * @return bool
 | 
			
		||||
   *   TRUE for success or if path does not exist, FALSE in the event of an
 | 
			
		||||
   *   error.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\Core\File\FileSystemInterface::deleteRecursive()
 | 
			
		||||
   */
 | 
			
		||||
  protected function fileUnmanagedDeleteRecursive($path, $callback = NULL): bool {
 | 
			
		||||
    if (isset($callback)) {
 | 
			
		||||
      call_user_func($callback, $path);
 | 
			
		||||
    }
 | 
			
		||||
    if (is_dir($path)) {
 | 
			
		||||
      $dir = dir($path);
 | 
			
		||||
      while (($entry = $dir->read()) !== FALSE) {
 | 
			
		||||
        if ($entry == '.' || $entry == '..') {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        $entry_path = $path . '/' . $entry;
 | 
			
		||||
        $this->fileUnmanagedDeleteRecursive($entry_path, $callback);
 | 
			
		||||
      }
 | 
			
		||||
      $dir->close();
 | 
			
		||||
 | 
			
		||||
      return rmdir($path);
 | 
			
		||||
    }
 | 
			
		||||
    return unlink($path);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,75 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite\Commands;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\DrupalKernel;
 | 
			
		||||
use Drupal\Core\Site\Settings;
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
 | 
			
		||||
use Symfony\Component\Console\Input\InputArgument;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Input\InputOption;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
use Symfony\Component\HttpFoundation\Request;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Command to generate a login link for the test site.
 | 
			
		||||
 *
 | 
			
		||||
 * @internal
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteUserLoginCommand extends Command {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The class loader to use for installation and initialization of setup.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Composer\Autoload\ClassLoader
 | 
			
		||||
   */
 | 
			
		||||
  protected $classLoader;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function configure() {
 | 
			
		||||
    $this->setName('user-login')
 | 
			
		||||
      ->setDescription('Generate a one time login link for an user.')
 | 
			
		||||
      ->addArgument('uid', InputArgument::REQUIRED, 'The ID of the user for whom the link will be generated')
 | 
			
		||||
      ->addOption('site-path', NULL, InputOption::VALUE_REQUIRED, 'The path for the test site.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   *
 | 
			
		||||
   * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
 | 
			
		||||
   */
 | 
			
		||||
  protected function execute(InputInterface $input, OutputInterface $output): int {
 | 
			
		||||
    $root = dirname(__DIR__, 5);
 | 
			
		||||
    chdir($root);
 | 
			
		||||
 | 
			
		||||
    $this->classLoader = require 'autoload.php';
 | 
			
		||||
    $kernel = new DrupalKernel('prod', $this->classLoader, FALSE);
 | 
			
		||||
    $kernel::bootEnvironment();
 | 
			
		||||
    $kernel->setSitePath($input->getOption('site-path'));
 | 
			
		||||
    Settings::initialize($kernel->getAppRoot(), $kernel->getSitePath(), $this->classLoader);
 | 
			
		||||
 | 
			
		||||
    $request = Request::createFromGlobals();
 | 
			
		||||
 | 
			
		||||
    $kernel->boot();
 | 
			
		||||
    $kernel->preHandle($request);
 | 
			
		||||
 | 
			
		||||
    $container = $kernel->getContainer();
 | 
			
		||||
    $uid = $input->getArgument('uid');
 | 
			
		||||
    if (!is_numeric($uid)) {
 | 
			
		||||
      throw new InvalidArgumentException(sprintf('The "uid" argument needs to be an integer, but it is "%s".', $uid));
 | 
			
		||||
    }
 | 
			
		||||
    $userEntity = $container->get('entity_type.manager')
 | 
			
		||||
      ->getStorage('user')
 | 
			
		||||
      ->load($uid);
 | 
			
		||||
    $url = user_pass_reset_url($userEntity) . '/login';
 | 
			
		||||
    $output->writeln($url);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								web/core/tests/Drupal/TestSite/HtmxAssetLoadTestSetup.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								web/core/tests/Drupal/TestSite/HtmxAssetLoadTestSetup.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Extension\ModuleInstallerInterface;
 | 
			
		||||
use Drupal\Core\Extension\ThemeInstallerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Setup file used by tests/src/Nightwatch/Tests/htmxAssetLoadTest.js.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\Tests\Scripts\TestSiteApplicationTest
 | 
			
		||||
 */
 | 
			
		||||
class HtmxAssetLoadTestSetup implements TestSetupInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setup(): void {
 | 
			
		||||
    // Install Olivero and set it as the default theme.
 | 
			
		||||
    $theme_installer = \Drupal::service('theme_installer');
 | 
			
		||||
    assert($theme_installer instanceof ThemeInstallerInterface);
 | 
			
		||||
    $theme_installer->install(['olivero'], TRUE);
 | 
			
		||||
    $system_theme_config = \Drupal::configFactory()->getEditable('system.theme');
 | 
			
		||||
    $system_theme_config->set('default', 'olivero')->save();
 | 
			
		||||
 | 
			
		||||
    // Install required modules.
 | 
			
		||||
    $module_installer = \Drupal::service('module_installer');
 | 
			
		||||
    assert($module_installer instanceof ModuleInstallerInterface);
 | 
			
		||||
    $module_installer->install(['test_htmx']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								web/core/tests/Drupal/TestSite/TestPreinstallInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								web/core/tests/Drupal/TestSite/TestPreinstallInterface.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Allows running code prior to a test site install.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\TestSite\Commands\TestSiteInstallCommand
 | 
			
		||||
 */
 | 
			
		||||
interface TestPreinstallInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Runs code prior to a test site install.
 | 
			
		||||
   *
 | 
			
		||||
   * This method is run after FunctionalTestSetupTrait::prepareEnvironment()
 | 
			
		||||
   * but before Drupal is installed. As such, there is limited setup of the
 | 
			
		||||
   * environment and no Drupal API is available.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $db_prefix
 | 
			
		||||
   *   The database prefix.
 | 
			
		||||
   * @param string $site_directory
 | 
			
		||||
   *   The site directory.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\TestSite\TestSiteInstallTestScript
 | 
			
		||||
   */
 | 
			
		||||
  public function preinstall($db_prefix, $site_directory);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								web/core/tests/Drupal/TestSite/TestSetupInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								web/core/tests/Drupal/TestSite/TestSetupInterface.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Allows setting up an environment as part of a test site install.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\TestSite\Commands\TestSiteInstallCommand
 | 
			
		||||
 */
 | 
			
		||||
interface TestSetupInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Run the code to setup the test environment.
 | 
			
		||||
   *
 | 
			
		||||
   * You have access to any API provided by any installed module. For example,
 | 
			
		||||
   * to install modules use:
 | 
			
		||||
   * @code
 | 
			
		||||
   * \Drupal::service('module_installer')->install(['my_module'])
 | 
			
		||||
   * @endcode
 | 
			
		||||
   *
 | 
			
		||||
   * Check out TestSiteInstallTestScript for an example.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\TestSite\TestSiteInstallTestScript
 | 
			
		||||
   */
 | 
			
		||||
  public function setup();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								web/core/tests/Drupal/TestSite/TestSiteApplication.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								web/core/tests/Drupal/TestSite/TestSiteApplication.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
use Drupal\TestSite\Commands\TestSiteInstallCommand;
 | 
			
		||||
use Drupal\TestSite\Commands\TestSiteReleaseLocksCommand;
 | 
			
		||||
use Drupal\TestSite\Commands\TestSiteTearDownCommand;
 | 
			
		||||
use Drupal\TestSite\Commands\TestSiteUserLoginCommand;
 | 
			
		||||
use Symfony\Component\Console\Application;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Application wrapper for test site commands.
 | 
			
		||||
 *
 | 
			
		||||
 * In order to see what commands are available and how to use them run
 | 
			
		||||
 * "php core/scripts/test-site.php" from command line and use the help system.
 | 
			
		||||
 *
 | 
			
		||||
 * @internal
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteApplication extends Application {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function getDefaultCommands(): array {
 | 
			
		||||
    $default_commands = parent::getDefaultCommands();
 | 
			
		||||
    $default_commands[] = new TestSiteInstallCommand();
 | 
			
		||||
    $default_commands[] = new TestSiteTearDownCommand();
 | 
			
		||||
    $default_commands[] = new TestSiteReleaseLocksCommand();
 | 
			
		||||
    $default_commands[] = new TestSiteUserLoginCommand();
 | 
			
		||||
    return $default_commands;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,71 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Extension\ModuleInstallerInterface;
 | 
			
		||||
use Drupal\Core\Extension\ThemeInstallerInterface;
 | 
			
		||||
use Drupal\node\Entity\Node;
 | 
			
		||||
use Drupal\comment\Entity\Comment;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Setup file used by TestSiteInstallTestScript.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\KernelTests\Scripts\TestSiteApplicationTest
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteClaroInstallTestScript implements TestSetupInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setup() {
 | 
			
		||||
    // Install required module for the Olivero front page.
 | 
			
		||||
    $module_installer = \Drupal::service('module_installer');
 | 
			
		||||
    assert($module_installer instanceof ModuleInstallerInterface);
 | 
			
		||||
    $module_installer->install(['olivero_test']);
 | 
			
		||||
 | 
			
		||||
    // Install Claro instead of Olivero and set it as the default theme.
 | 
			
		||||
    $theme_installer = \Drupal::service('theme_installer');
 | 
			
		||||
    assert($theme_installer instanceof ThemeInstallerInterface);
 | 
			
		||||
    $theme_installer->install(['claro'], TRUE);
 | 
			
		||||
    $system_theme_config = \Drupal::configFactory()->getEditable('system.theme');
 | 
			
		||||
    $system_theme_config->set('default', 'claro')->save();
 | 
			
		||||
 | 
			
		||||
    // Create an article that will have no comments
 | 
			
		||||
    $article_no_comments = Node::create(['type' => 'article']);
 | 
			
		||||
    $article_no_comments->set('title', 'Article without comments');
 | 
			
		||||
    // Enable comments
 | 
			
		||||
    $article_no_comments->set('comment', 2);
 | 
			
		||||
    $article_no_comments->save();
 | 
			
		||||
 | 
			
		||||
    // Create an article that will have comments
 | 
			
		||||
    $article_with_comments = Node::create(['type' => 'article']);
 | 
			
		||||
    $article_with_comments->set('title', 'Article with comments');
 | 
			
		||||
    // Enable comments
 | 
			
		||||
    $article_with_comments->set('comment', 2);
 | 
			
		||||
    $article_with_comments->save();
 | 
			
		||||
 | 
			
		||||
    $values = [
 | 
			
		||||
      // These values are for the entity that you're creating the comment for,
 | 
			
		||||
      // not the comment itself.
 | 
			
		||||
      'entity_type' => 'node',
 | 
			
		||||
      'entity_id'   => 2,
 | 
			
		||||
      'field_name'  => 'comment',
 | 
			
		||||
      'uid' => 1,
 | 
			
		||||
      // These values are for the comment itself.
 | 
			
		||||
      'comment_type' => 'comment',
 | 
			
		||||
      'subject' => 'A comment',
 | 
			
		||||
      'comment_body' => 'Body of comment',
 | 
			
		||||
      // Whether the comment is 'approved' or not.
 | 
			
		||||
      'status' => 1,
 | 
			
		||||
    ];
 | 
			
		||||
    // Create comment entities out of our field values
 | 
			
		||||
    $comment1 = Comment::create($values);
 | 
			
		||||
    $comment1->save();
 | 
			
		||||
 | 
			
		||||
    $comment2 = Comment::create($values);
 | 
			
		||||
    $comment2->save();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								web/core/tests/Drupal/TestSite/TestSiteInstallTestScript.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/core/tests/Drupal/TestSite/TestSiteInstallTestScript.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Setup file used by TestSiteApplicationTest.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\KernelTests\Scripts\TestSiteApplicationTest
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteInstallTestScript implements TestSetupInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setup() {
 | 
			
		||||
    \Drupal::service('module_installer')->install(['test_page_test']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,32 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
// cspell:ignore nmsgid nmsgstr enregistrer
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Setup file used by TestSiteApplicationTest.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\KernelTests\Scripts\TestSiteApplicationTest
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteMultilingualInstallTestScript implements TestSetupInterface, TestPreinstallInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function preinstall($db_prefix, $site_directory) {
 | 
			
		||||
    // Place a custom local translation in the translations directory.
 | 
			
		||||
    mkdir($site_directory . '/files/translations', 0777, TRUE);
 | 
			
		||||
    file_put_contents($site_directory . '/files/translations/drupal-8.0.0.fr.po', "msgid \"\"\nmsgstr \"\"\nmsgid \"Save and continue\"\nmsgstr \"Enregistrer et continuer\"");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setup() {
 | 
			
		||||
    \Drupal::service('module_installer')->install(['test_page_test']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,71 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\TestSite;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Extension\ModuleInstallerInterface;
 | 
			
		||||
use Drupal\Core\Extension\ThemeInstallerInterface;
 | 
			
		||||
use Drupal\node\Entity\Node;
 | 
			
		||||
use Drupal\comment\Entity\Comment;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Setup file used by TestSiteInstallTestScript.
 | 
			
		||||
 *
 | 
			
		||||
 * @see \Drupal\KernelTests\Scripts\TestSiteApplicationTest
 | 
			
		||||
 */
 | 
			
		||||
class TestSiteOliveroInstallTestScript implements TestSetupInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setup() {
 | 
			
		||||
    // Install required module for the Olivero front page.
 | 
			
		||||
    $module_installer = \Drupal::service('module_installer');
 | 
			
		||||
    assert($module_installer instanceof ModuleInstallerInterface);
 | 
			
		||||
    $module_installer->install(['olivero_test']);
 | 
			
		||||
 | 
			
		||||
    // Install Olivero and set it as the default theme.
 | 
			
		||||
    $theme_installer = \Drupal::service('theme_installer');
 | 
			
		||||
    assert($theme_installer instanceof ThemeInstallerInterface);
 | 
			
		||||
    $theme_installer->install(['olivero'], TRUE);
 | 
			
		||||
    $system_theme_config = \Drupal::configFactory()->getEditable('system.theme');
 | 
			
		||||
    $system_theme_config->set('default', 'olivero')->save();
 | 
			
		||||
 | 
			
		||||
    // Create an article that will have no comments
 | 
			
		||||
    $article_no_comments = Node::create(['type' => 'article']);
 | 
			
		||||
    $article_no_comments->set('title', 'Article without comments');
 | 
			
		||||
    // Enable comments
 | 
			
		||||
    $article_no_comments->set('comment', 2);
 | 
			
		||||
    $article_no_comments->save();
 | 
			
		||||
 | 
			
		||||
    // Create an article that will have comments
 | 
			
		||||
    $article_with_comments = Node::create(['type' => 'article']);
 | 
			
		||||
    $article_with_comments->set('title', 'Article with comments');
 | 
			
		||||
    // Enable comments
 | 
			
		||||
    $article_with_comments->set('comment', 2);
 | 
			
		||||
    $article_with_comments->save();
 | 
			
		||||
 | 
			
		||||
    $values = [
 | 
			
		||||
      // These values are for the entity that you're creating the comment for,
 | 
			
		||||
      // not the comment itself.
 | 
			
		||||
      'entity_type' => 'node',
 | 
			
		||||
      'entity_id'   => 2,
 | 
			
		||||
      'field_name'  => 'comment',
 | 
			
		||||
      'uid' => 1,
 | 
			
		||||
      // These values are for the comment itself.
 | 
			
		||||
      'comment_type' => 'comment',
 | 
			
		||||
      'subject' => 'A comment',
 | 
			
		||||
      'comment_body' => 'Body of comment',
 | 
			
		||||
      // Whether the comment is 'approved' or not.
 | 
			
		||||
      'status' => 1,
 | 
			
		||||
    ];
 | 
			
		||||
    // Create comment entities out of our field values
 | 
			
		||||
    $comment1 = Comment::create($values);
 | 
			
		||||
    $comment1->save();
 | 
			
		||||
 | 
			
		||||
    $comment2 = Comment::create($values);
 | 
			
		||||
    $comment2->save();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user