Як я можу завантажувати 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
Як працює cron magento 2? Ви можете використовувати один і той же підхід?
Amasty

4
Хороша ідея, але ... код з cron.php: $app = $bootstrap->createApplication('Magento\Framework\App\Cron', ['parameters' => ['group::']]);. Чи варто створити власну модель додатка?
Маріус

1
написати одиничний тест
Крістоф у Фомані

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запускатиме Magento env (можливо, ще якісь божевільні речі ... Я ще не все перевірив), а потім викликає launchметод із програми.
Ось чому потрібно помістити весь брудний код всередину цього методу.
Тоді \Magento\Framework\App\Bootstrap::runвиклики , $response->sendResponse();де $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
це працює. інші відповіді не відповідають.
ahnbizcad

1
це запускає HTTP 500 у мене.
Макс

Все ще працює в 2.1.2. Мені довелося видозмінити необхідний шлях
simonthesorcerer

не працював для мене
Сарфарай Сіпай

20

На основі відповіді @ Маріуса я придумав це.

Він працює як через командний рядок, так і через браузер, що я вважаю корисним.

Ось зразок сценарію для програмного видалення категорії.

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 в минулому, ці зусилля також могли піти на написання тесту, який матиме постійну користь.
Крістоф у Фомані

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. {модуль} /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 для сценаріїв з великим експортом. спасибі
урбансурфер

13

Важливою частиною є \Magento\Framework\App\Bootstrap::create

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

Це з одного боку createApplication()і наступний run()метод, але також getDirList()і getObjectManager()метод, і обидва не потребують аргументів.

Отже, додаток не потрібен, недоліки полягають у тому, що обробник помилок не ініціалізується.


6

Можливо поза темою, але я завжди використовую файл контролера індексу контактів у Magento 1 для тестування речей (метод IndexAction). Це так само просто, як перейти до example.com/contacts. Ви просто повинні переконатися, що ці зміни не здійснюються;)

Я впевнений, що ви можете зробити щось подібне в Magento 2. Позбавляє вас від необхідності створювати новий файл з кодом завантаження.


1
Небо забороняє ти забути, або роби це на виробництві! Будь ласка, не змінюйте основний код.
Ryan Hoerr

@RyanH. Не станеться Контроль версій, автоматизовані збірки, статичний аналіз коду, огляд коду однорангових, постановка / тестування прийняття користувача / тощо. Але так, якщо у вас цього немає, є ймовірність, що це може закінчитися виробництвом: P
Ерфан

1
Це чудово для вас, але більшість людей, які шукають тут, не матимуть таких типів контролю. Краще завжди вчити (і робити) правильному способу робити справи.
Ryan Hoerr

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;
    }

}

Це також не працює для мене в 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');

Сподіваюся, що це буде корисно.


2

Ви можете запустити прямий скрипт з Magento 2 root, використовуючи код нижче. Створіть новий файл у кореневому каталозі 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');

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.