Как я могу загрузить Magento 2 в сценарии test.php?

93

В magento 1 я мог создать файл, в котором мне нужно было только создать экземпляр Mage_Core_Model_Appкласса, а затем я мог бы добавить свой «грязный» код для целей тестирования.
Как то так test.php:

<?php
//some settings
error_reporting(E_ALL | E_STRICT); 
define('MAGENTO_ROOT', getcwd()); 
$mageFilename = MAGENTO_ROOT . '/app/Mage.php'; 
require_once $mageFilename; 
Mage::setIsDeveloperMode(true); 
ini_set('display_errors', 1); 
umask(0);
//instantiate the app model
Mage::app(); 
//my toy code in here.

Тогда я смог позвонить test.phpв браузер и посмотреть, что я делаю.

Как я могу сделать то же самое для Magento 2?

Мариус
источник
4
Как работает magento 2 cron? Может быть, вы можете использовать тот же подход?
Amasty
4
Хорошая идея, но ... код из cron.php: $app = $bootstrap->createApplication('Magento\Framework\App\Cron', ['parameters' => ['group::']]);. Должен ли я создать свою собственную модель приложения?
Мариус
1
написать юнит-тест
Кристоф на Fooman
2
@Fooman. Не стесняйтесь написать это как ответ, но, пожалуйста, приведите пример. Я новичок в модульном тестировании.
Мариус

Ответы:

86

Основываясь на ответе @ Flyingmana, я немного покопался и нашел решение. Это похоже на работу для меня.
Сначала моё решение, потом несколько объяснений.
Я создал файл с именем test.phpв корне моего экземпляра magento.

<?php
require __DIR__ . '/app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
/** @var \Magento\Framework\App\Http $app */
$app = $bootstrap->createApplication('TestApp');
$bootstrap->run($app);

Затем я создал файл, который называется TestApp.phpтам же с этим контентом.

<?php
class TestApp
    extends \Magento\Framework\App\Http
    implements \Magento\Framework\AppInterface {
    public function launch()
    {
        //dirty code goes here. 
        //the example below just prints a class name
        echo get_class($this->_objectManager->create('\Magento\Catalog\Model\Category'));
        //the method must end with this line
        return $this->_response;
    }

    public function catchException(\Magento\Framework\App\Bootstrap $bootstrap, \Exception $exception)
    {
        return false;
    }

}

Теперь я могу просто позвонить test.phpв браузер и все, что помещено в TestApp :: launch (), будет выполнено.

Теперь, почему это работает:
метод createApplicationиз класса начальной загрузки является наиболее важной частью. Создает экземпляр класса приложения. Метод createApplicationожидает реализации, \Magento\Framework\AppInterfaceкоторая содержит 2 метода.
Поэтому я создал свой собственный класс, TestAppкоторый реализует этот интерфейс. Я всегда catchExceptionвозвращал метод, falseпотому что не хочу, чтобы мое приложение обрабатывало исключения. Если что-то не так, просто распечатайте это на экране.
Затем я реализовал метод launch. этот называется \Magento\Framework\App\Bootstrap::run. Этот runметод делает почти то же самое, независимо от того, какое приложение передается в качестве параметра.
Единственное, что зависит от приложения, это строка:

$response = $application->launch();

Это означает, что вызов \Magento\Framework\App\Bootstrap::runвызовет среду en Magento (может быть, сделаю что-то еще сумасшедшее ... я еще не все проверил), а затем вызовет launchметод из приложения.
Вот почему вы должны поместить весь свой грязный код в этот метод.
Затем \Magento\Framework\App\Bootstrap::runвызовы $response->sendResponse();where $response- это то, что launchвозвращает метод.
Вот почему return $this->_response;это необходимо. Он просто возвращает пустой ответ.

Я сделал свой класс приложения расширенным, \Magento\Framework\App\Httpчтобы у меня уже были параметры запроса и ответа (и другие), но вы не можете заставить свой класс ничего не расширять. Затем вам нужно скопировать конструктор из \Magento\Framework\App\Httpкласса. Возможно, добавьте больше параметров в конструктор, если вам это нужно.

Мариус
источник
2
Конечно, TestAppкласс мог быть определен в том же test.phpфайле, но я не хочу делать его таким грязным :)
Marius
Мне пришлось добавить parent::launch();в качестве первой строки launch()метода, так как он выдавал ошибку «Код города не установлен»
OSdave
@OSdave. Это работало без этого, когда я проверял. Скорее всего, что-то изменилось в последних версиях.
Мариус
@Marius, я установил magento, быстро установив сервер. И не используйте bootstrap.php в приложении
er.irfankhan11
1
@Butterfly Вам не нужно включать его в свой пользовательский контроллер. Файл включается в index.php, прежде чем он достигнет вашего контроллера.
Мариус
54

Для быстрых / коротких / грязных тестов я использовал что-то вроде этого:

use Magento\Framework\App\Bootstrap;
require __DIR__ . '/../app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);

$obj = $bootstrap->getObjectManager();

$state = $obj->get('Magento\Framework\App\State');
$state->setAreaCode('frontend');

$quote = $obj->get('Magento\Checkout\Model\Session')->getQuote()->load(1);
print_r($quote->getOrigData());
Карко
источник
4
это работает. другие ответы нет.
августа
1
это вызывает HTTP 500 на моей стороне.
Макс
Все еще работает в 2.1.2. Я должен был изменить require-path tho
simonthesorcerer
не работал для меня
Сарфарадж Сипай
20

Основываясь на ответе @ Marius, я придумал это.

Он работает как через командную строку, так и через браузер, что я считаю полезным.

Вот пример сценария для программного удаления категории.

scripts/abstract.php

<?php
use \Magento\Framework\AppInterface as AppInterface;
use \Magento\Framework\App\Http as Http;

use Magento\Framework\ObjectManager\ConfigLoaderInterface;
use Magento\Framework\App\Request\Http as RequestHttp;
use Magento\Framework\App\Response\Http as ResponseHttp;
use Magento\Framework\Event;
use Magento\Framework\Filesystem;
use Magento\Framework\App\AreaList as AreaList;
use Magento\Framework\App\State as State;

abstract class AbstractApp implements AppInterface
{
    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectManager,
        Event\Manager $eventManager,
        AreaList $areaList,
        RequestHttp $request,
        ResponseHttp $response,
        ConfigLoaderInterface $configLoader,
        State $state,
        Filesystem $filesystem,
        \Magento\Framework\Registry $registry
    ) {
        $this->_objectManager = $objectManager;
        $this->_eventManager = $eventManager;
        $this->_areaList = $areaList;
        $this->_request = $request;
        $this->_response = $response;
        $this->_configLoader = $configLoader;
        $this->_state = $state;
        $this->_filesystem = $filesystem;
        $this->registry = $registry;
    }

    public function launch()
    {
        $this->run();
        return $this->_response;
    }

    abstract public function run();

    public function catchException(\Magento\Framework\App\Bootstrap $bootstrap, \Exception $exception)
    {
        return false;
    }
}

scripts/delete-category.php

<?php
require dirname(__FILE__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
require dirname(__FILE__) . '/abstract.php';

class CreateCategoriesApp extends AbstractApp
{

    public function run()
    {
        $this->_objectManager->get('Magento\Framework\Registry')
            ->register('isSecureArea', true);

        $category = $this->_objectManager->create('\Magento\Catalog\Model\Category');
        $category = $category->load(343);

        $category->delete();
    }
}

/** @var \Magento\Framework\App\Http $app */
$app = $bootstrap->createApplication('CreateCategoriesApp');
$bootstrap->run($app);

Тогда я просто запускаю это как php scripts/delete-category.php

Люк Роджерс
источник
2
работает нормально для внешнего интерфейса, если я хочу получить доступ к коду администратора, то он показывает ошибку доступа или проблемы с областью, можете ли вы рассказать, как вызвать область администратора
Pradeep Kumar
При попытке вызвать что - то, я получаю: Magento\Framework\Exception\LocalizedException: Area code is not set. Как я могу установить это? Мне нужен приятель.
Макс
Боюсь, я мало смотрел на M2, так как написал этот код, боюсь, изменения в фреймворке, возможно, сделали его недействительным или нуждаются в исправлении, извините!
Люк Роджерс
18

Как и просил очень короткий пример того, как вы могли бы написать тест (не помещая его в структуру расширений вашей папки). Увы, это все командная строка, а не для потребления через браузер.

Создать файл

dev/tests/unit/quicktest.php

с участием

<?php

class QuickTest extends \PHPUnit_Framework_TestCase
{
    public function testExample()
    {
        //instantiate your class
        $context = new Magento\Framework\Object();

        $context->setData('param', 'value');

        //test whatever you want to test
        $this->assertEquals('value', $context->getData('param'));

        //you could even output to console
        echo $context->getData('param');

    }
}

затем из каталога dev/tests/unit/запустить, phpunit quicktest.phpкоторый будет выполнять ваш код. Это все работает, так как файл dev/tests/unit/phpunit.xml.distавтоматически загружается и подготавливает среду.

Во многих случаях вам может потребоваться ввести данные в конструктор классов. Пожалуйста, ознакомьтесь с существующими тестами в разделе dev/tests/unit/testsuite/дальнейшие примеры того, как это может выглядеть, включая насмешливые объекты.

Кристоф в Фуман
источник
1
Я попросил "грязную" игровую площадку. Вы дали здесь чистый :). Интересная идея. Я дам ему попробовать.
Мариус
7
Я обнаружил, что в те времена, когда я в прошлом создавал test.php, усилия могли бы быть направлены на написание теста, который будет приносить постоянную пользу.
Кристоф на Fooman
15

Вот лучший способ, чем подключиться к тестовой системе: использовать интерфейс командной строки Magento 2.

Это означает, что вам придется интегрировать свой код песочницы в реальный модуль (или создать его для этой цели), но вы все равно должны это делать.

После того, как вы настроили свой модуль , добавить команду довольно просто. Все, что вам нужно, это класс и DI, чтобы зарегистрировать его.

1. {module} /etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="greeting_command" xsi:type="object">Magento\CommandExample\Console\Command\GreetingCommand</item>
            </argument>
        </arguments>
    </type>
</config>

2. {module} /Console/Command/GreetingCommand.php

<?php

namespace Magento\CommandExample\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class GreetingCommand
 */
class GreetingCommand extends Command
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('example:greeting')
             ->setDescription('Greeting command');

        parent::configure();
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('<info>Hello world!</info>');
    }
}

Пример, полученный из https://github.com/magento/magento2-samples/tree/master/sample-module-command - см. Там полный модуль, включающий эту функцию. Есть и менее тривиальные примеры .

По соглашению, ваш командный класс всегда должен быть {module}/Console/Commandи заканчиваться на Command.php.

После того, как вы добавили эти два бита кода (и промытой кэш Magento и т.д.), выполнить вашу команду по имени в SSH: php bin/magento example:greeting.

Вы можете использовать внедрение зависимостей в этом контексте, поэтому вы можете запускать любой код, который захотите execute().

Этот интерфейс построен на компоненте консоли Symfony , поэтому у вас также есть полный доступ ко всему широкому спектру функций, включая параметры / аргументы , таблицы и очень простые индикаторы выполнения .

Если вы сталкиваетесь с какими-либо проблемами при настройке вашей команды или опций, вы обычно можете запустить команду list, чтобы лучше понять, что происходит: php bin/magento list

Наслаждаться.

Райан Херр
источник
Приятно! с индикаторами прогресса Symfony для скриптов с большим экспортом. спасибо
urbansurfers
13

Важной частью является \Magento\Framework\App\Bootstrap::create

но так как Bootstrap::init()метод является частным, и там происходит много важных вещей, необходимы публичные методы, вызывающие его.

То с одной стороны, createApplication()и следуя run()способу, но также getDirList()и getObjectManager()способ, в котором оба не нужно никаких аргументов.

Таким образом, приложение не нужно, минусы в том, что обработчик ошибок не инициализирован.

Flyingmana
источник
6

Возможно, не по теме, но я всегда использую файл контроллера индекса контактов в Magento 1 для тестирования (метод IndexAction). Это так же просто, как перейти на example.com/contacts. Вы просто должны убедиться, что эти изменения не зафиксированы;)

Я уверен, что вы можете сделать что-то подобное в Magento 2. Избавляет вас от необходимости создавать новый файл с кодом начальной загрузки.

Erfan
источник
1
Боже упаси тебя забыть или сделать это на производстве! Пожалуйста, не изменяйте основной код.
Райан Херр
@RyanH. Не случится Контроль версий, автоматические сборки, статический анализ кода, проверка кода коллег, постановка / приемочное тестирование пользователя и т. Д. Но да, если у вас этого нет, есть шанс, что это может закончиться на производстве: P
Erfan
1
Это здорово для вас, но большинство людей, ищущих здесь, не будут иметь такие виды контроля. Лучше учить (и делать) правильный способ делать вещи, всегда.
Райан Херр
5

Этот ответ является небольшой модификацией ответа Мариуса выше

Потому что в Magento 2.1 появилась ошибка как Area code not setпри использовании этого кода.So the intension of this answer is to fix that error on Magento 2.1

Что вы должны сделать, чтобы исправить эту ошибку, это определить область в вашем test.php file. (см. измененный файл ниже).

<?php
require __DIR__ . '/app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$obj = $bootstrap->getObjectManager();

$state = $obj->get('Magento\Framework\App\State');
$state->setAreaCode('frontend');
/** @var \Magento\Framework\App\Http $app */
$app = $bootstrap->createApplication('TestApp');
$bootstrap->run($app);

И TestApp.phpфайл останется прежним.

<?php

class TestApp
    extends \Magento\Framework\App\Http
    implements \Magento\Framework\AppInterface {
    public function launch()
    {
        //dirty code goes here.
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $product = $objectManager->get('Magento\Catalog\Model\Product')->load(71);
        var_dump($product->getData());

        return $this->_response;
    }

    public function catchException(\Magento\Framework\App\Bootstrap $bootstrap, \Exception $exception)
    {
        return false;
    }

}
Sukeshini
источник
Это также не работает для меня в 2.1.6, я получаюUncaught TypeError: Argument 2 passed to Magento\\Framework\\App\\Http::__construct() must be an instance of Magento\\Framework\\Event\\Manager, none given
Партизан
5

Вы можете направить скрипт в корневой каталог magento, добавив нижеприведенный код, и загрузчик будет включен. [Создайте test.php в корневой папке magento и включите код ниже]

ini_set('display_errors', 1);
ini_set('max_execution_time', 0);
ini_set("memory_limit", "-1");
set_time_limit(0);
error_reporting(E_ALL);
require './app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get('Magento\Framework\App\State');
$state->setAreaCode('admin');

Надеюсь, это будет полезно.

Yogesh
источник
2

Вы можете запустить прямой скрипт из корня Magento 2, используя приведенный ниже код. Создайте новый файл в корневой директории Magento 2 и добавьте этот код, а затем добавьте свой скрипт в файл.

<?php
    use Magento\Framework\App\Bootstrap;
    include('app/bootstrap.php');
    $bootstrap = Bootstrap::create(BP, $_SERVER);

    $objectManager = $bootstrap->getObjectManager();

    $state = $objectManager->get('Magento\Framework\App\State');
    $state->setAreaCode('frontend');
Evince Development
источник
1

Вот что я сделал, чтобы инициализировать Magento в свой собственный скрипт за пределами каталога magento.

//Required to include Magento functions.
$magento_dir "your/path/to/the/magento/installation/directory/";
require $magento_dir . 'app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
//$app = $bootstrap->createApplication('Magento\Framework\App\Http');
$app = $bootstrap->createApplication('MyClass');
$bootstrap->run($app);

Это рекомендуемый способ в соответствии с документами Magento. http://devdocs.magento.com/guides/v2.0/config-guide/bootstrap/magento-bootstrap.html

MagentoMan
источник