Como posso inicializar o Magento 2 em um script test.php?

93

No magento 1, eu poderia criar um arquivo em que só precisava instanciar a Mage_Core_Model_Appclasse e, em seguida, adicionar meu código "sujo" para fins de teste.
Algo assim 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.

Pude ligar test.phpno navegador e ver o que estou fazendo.

Como posso fazer o mesmo com o Magento 2?

Marius
fonte
4
Como funciona o magento 2 cron? Pode ser que você possa usar a mesma abordagem?
Amasty
4
Boa ideia, mas ... código cron.php: $app = $bootstrap->createApplication('Magento\Framework\App\Cron', ['parameters' => ['group::']]);. Devo criar meu próprio modelo de aplicativo?
Marius
1
escrever um teste de unidade
Kristof em Fooman
2
@Fooman. Sinta-se à vontade para escrever isso como resposta, mas forneça um exemplo. Eu sou uma espécie de novato em testes de unidade.
Marius

Respostas:

86

Com base na resposta da @ Flyingmana, eu pesquisei um pouco e achei uma solução. Parece trabalhar para mim.
Primeiro a minha solução, depois algumas explicações.
Eu criei um arquivo chamado test.phpna raiz da minha instância do 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);

Então eu criei um arquivo chamado TestApp.phpno mesmo local com este conteúdo.

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

}

Agora posso chamar test.phpo navegador e tudo o que é colocado no TestApp :: launch () será executado.

Agora, por que isso funciona:
o método createApplicationda classe bootstrap é a parte mais importante. Ele cria uma instância de uma classe de aplicativo. O método createApplicationespera uma implementação do \Magento\Framework\AppInterfaceque contém 2 métodos.
Então, eu criei minha própria classe em TestAppque implementa essa interface. Eu fiz o método catchExceptionretornar falsesempre porque não quero que meu aplicativo lide com exceções. Caso algo esteja errado, basta imprimi-lo na tela.
Então eu implementei o método launch. este é chamado por \Magento\Framework\App\Bootstrap::run. Esse runmétodo faz quase a mesma coisa, independentemente do que o aplicativo passou como parâmetro.
A única coisa que depende da aplicação é esta linha:

$response = $application->launch();

Isso significa que a chamada \Magento\Framework\App\Bootstrap::runiniciará o ambiente do Magento (talvez faça outras coisas malucas ... Eu não verifiquei tudo ainda) e depois chame o launchmétodo a partir do aplicativo.
É por isso que você precisa colocar todo o seu código sujo dentro desse método.
Em seguida, as \Magento\Framework\App\Bootstrap::runchamadas $response->sendResponse();onde $responseé o que o launchmétodo retorna.
É por isso que return $this->_response;é necessário. Apenas retorna uma resposta vazia.

Eu fiz minha classe de aplicativo estender \Magento\Framework\App\Httppara que eu já tenha parâmetros de solicitação e resposta (e outros), mas você pode fazer com que sua classe não estenda nada. Então você precisa copiar o construtor da \Magento\Framework\App\Httpclasse. Talvez adicione mais parâmetros no construtor, se necessário.

Marius
fonte
2
Claro que a TestAppclasse poderia ter sido definido no mesmo test.phparquivo, mas eu não quero fazê-lo que suja :)
Marius
Eu tive que adicionar parent::launch();como a primeira linha de launch()método como ele estava me dando um "código de área não definida" erro
OSdave
@OSdave. Funcionou sem isso quando testei. Provavelmente algo mudou nas últimas versões.
Marius
@ Marius, eu instalei o magento instalando rapidamente o servidor de mangueira. E não tem bootstrap.php no aplicativo
er.irfankhan11
1
@ Butterfly Você não precisa incluí-lo no seu controlador personalizado. O arquivo é incluído no index.php antes de chegar ao seu controlador.
Marius
54

Para testes rápidos / curtos / sujos, usei algo como isto:

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());
carco
fonte
4
isso funciona. as outras respostas não.
ahnbizcad 5/09/16
1
isso aciona o HTTP 500 ao meu lado.
Max
Ainda funciona no 2.1.2. Eu tive que modificar o caminho requerem tho
simonthesorcerer
não funcionou para mim
Sarfaraj Sipai
20

Com base na resposta de @ Marius, eu vim com isso.

Funciona tanto pela linha de comando quanto pelo navegador, o que acho útil.

Aqui está um exemplo de script para excluir programaticamente a categoria.

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

Então eu apenas corro como php scripts/delete-category.php

Luke Rodgers
fonte
2
funcionando bem para frontend, se eu quiser acessar código de administrador, em seguida, mostrar o acesso de erro ou problema de área, você pode dizer como chamar para a área de administração
Pradeep Kumar
Ao tentar chamar algo, eu recebo: Magento\Framework\Exception\LocalizedException: Area code is not set. Como posso configurá-lo? Eu preciso do amigo.
Max
Eu não olhei muito para o M2 desde que escrevi esse código, receio que alterações no quadro possam torná-lo inválido ou precisando de alterações, desculpe!
Luke Rodgers
18

Conforme solicitado, um exemplo muito curto de como você pode escrever um teste (sem colocá-lo na estrutura de extensão de pastas). Infelizmente, tudo isso é linha de comando e não para consumo através de um navegador.

Crie o arquivo

dev/tests/unit/quicktest.php

com

<?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');

    }
}

depois, a partir do diretório dev/tests/unit/executado, phpunit quicktest.phpque executará seu código. Isso tudo funciona, pois o arquivo dev/tests/unit/phpunit.xml.disté carregado automaticamente e prepara o ambiente.

Em muitos casos, você pode precisar fornecer informações ao construtor das classes. Consulte os testes existentes abaixo dev/tests/unit/testsuite/para obter mais exemplos de como isso pode parecer, incluindo objetos de simulação.

Kristof na Fooman
fonte
1
Eu pedi um playground "sujo". Você deu aqui um limpo :). Idéia interessante. Vou tentar.
Marius
7
Acho que nos tempos em que eu teria criado um test.php no passado, o esforço poderia ter sido feito para escrever um teste que traria benefícios contínuos.
Kristof em Fooman
15

Aqui está uma maneira melhor do que se conectar ao sistema de teste: Use a interface de linha de comando do Magento 2.

Isso significa que você terá que integrar seu código de sandbox em um módulo real (ou criar um para o efeito), mas você deve fazer isso de qualquer maneira.

Depois de configurar seu módulo , adicionar um comando é bem fácil. Tudo que você precisa é da classe e do DI para registrá-la.

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

Exemplo derivado de https://github.com/magento/magento2-samples/tree/master/sample-module-command - veja aqui um módulo completo que incorpora essa funcionalidade. Existem exemplos menos triviais incluídos.

Por convenção, sua classe de comando deve sempre estar dentro {module}/Console/Commande terminar com Command.php.

Depois de adicionar esses dois pedaços de código (e cache do Magento corado, etc.), execute o seu comando pelo nome em SSH: php bin/magento example:greeting.

Você pode usar a injeção de dependência nesse contexto, para executar qualquer código que desejar execute().

Essa interface é construída no componente Console do Symfony , para que você também tenha acesso total a toda essa ampla gama de funcionalidades, incluindo opções / argumentos , tabelas e barras de progresso muito fáceis .

Se você tiver algum problema ao configurar seu comando ou opções, geralmente poderá executar o comando 'list' para obter melhor visibilidade do que está errado: php bin/magento list

Desfrutar.

Ryan Hoerr
fonte
Agradável! com as barras de progresso do Symfony para scripts com grande exportação. obrigado
urbansurfers 23/03
13

A parte importante é a \Magento\Framework\App\Bootstrap::create

mas como o Bootstrap::init()método é privado, e acontece muitas coisas importantes, são necessários métodos públicos que o chamam.

Isso, por um lado, createApplication()e seguindo o run()método, mas também o método getDirList()e getObjectManager(), que ambos não precisam de argumento.

Portanto, um Aplicativo não é necessário, as desvantagens são que o Manipulador de erros não é inicializado.

Flyingmana
fonte
6

Possivelmente fora de tópico, mas eu sempre uso o arquivo do controlador de índice de contatos no Magento 1 para testar as coisas (método IndexAction). É tão simples quanto ir para example.com/contacts. Você só precisa se certificar de não confirmar essas alterações;)

Tenho certeza que você pode fazer algo semelhante no Magento 2. Poupa você de ter que criar um novo arquivo com o código de inicialização.

Erfan
fonte
1
O céu proíbe que você esqueça, ou faça isso na produção! Por favor, não modifique o código principal.
23716 Ryan Hoerr
@RyanH. Isso não vai acontecer. Controle de versão, compilações automatizadas, análise de código estático, revisão de código por pares, teste de preparação / aceitação do usuário / etc. Mas sim, se você não tiver isso, há uma chance de que ele acabe em produção: P
Erfan
1
Isso é ótimo para você, mas a maioria das pessoas que olha aqui não terá esse tipo de controle. Melhor ensinar (e fazer) a maneira correta de fazer as coisas, sempre.
Ryan Hoerr 23/02
5

Esta resposta é uma pequena modificação na resposta acima por Marius

Porque no Magento 2.1 obtive o erro como Area code not setao usar esse código.So the intension of this answer is to fix that error on Magento 2.1

O que você precisa fazer para corrigir esse erro é definir a área em seu test.php file . (veja o arquivo modificado abaixo).

<?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);

E o TestApp.phparquivo permanecerá o mesmo.

<?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
fonte
Isso também não funciona para mim em 2.1.6, receboUncaught TypeError: Argument 2 passed to Magento\\Framework\\App\\Http::__construct() must be an instance of Magento\\Framework\\Event\\Manager, none given
Guerrilla
5

Você pode direcionar o script na raiz do magento adicionando o código abaixo e a inicialização será incluída. [Crie o test.php na pasta raiz do magento e inclua o código abaixo]

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');

Espero que isso seja útil.

Yogesh
fonte
2

Você pode executar o script direto da raiz do Magento 2 usando o código abaixo. Crie um novo arquivo no diretório raiz do Magento 2 e adicione esse código e depois adicione seu script no arquivo.

<?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
fonte
1

Aqui está o que eu fiz para trazer o Magento para inicializar no meu script personalizado fora do diretório 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);

Esta é a maneira recomendada, de acordo com os documentos do Magento. http://devdocs.magento.com/guides/v2.0/config-guide/bootstrap/magento-bootstrap.html

MagentoMan
fonte