Come posso avviare bootstrap Magento 2 in uno script test.php?


93

In magento 1 ho potuto creare un file in cui avevo solo bisogno di creare un'istanza della Mage_Core_Model_Appclasse e quindi ho potuto aggiungere il mio codice "sporco" a scopo di test.
Qualcosa del genere 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.

Quindi sono stato in grado di chiamare test.phpnel browser e vedere cosa sto facendo.

Come posso fare lo stesso per Magento 2?


4
Come funziona magento 2 cron? Forse puoi usare lo stesso approccio?
Amasty,

4
Buona idea, ma ... codice cron.php: $app = $bootstrap->createApplication('Magento\Framework\App\Cron', ['parameters' => ['group::']]);. Devo creare il mio modello di app?
Marius

1
scrivere un test unitario
Kristof a Fooman il

2
@Fooman. Sentiti libero di scrivere questo come una risposta, ma ti preghiamo di fornire un esempio. Sono un po 'nuovo per i test unitari.
Marius

Risposte:


86

Sulla base della risposta di @ Flyingmana ho fatto un po 'di ricerche e ho trovato una soluzione. Sembra funzionare per me.
Prima la mia soluzione, poi alcune spiegazioni.
Ho creato un file chiamato test.phpnella radice della mia istanza di 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);

Quindi ho creato un file chiamato TestApp.phpnello stesso posto con questo contenuto.

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

}

Ora posso semplicemente chiamare test.phpnel browser e tutto ciò che viene inserito in TestApp :: launch () verrà eseguito.

Ora, perché funziona:
il metodo createApplicationdella classe bootstrap è la parte più importante. Crea un'istanza di una classe di applicazione. Il metodo createApplicationprevede un'implementazione di quello \Magento\Framework\AppInterfaceche contiene 2 metodi.
Così ho creato la mia classe in TestAppquella implementa quell'interfaccia. Ho catchExceptionrestituito il metodo falsesempre perché non voglio che la mia app gestisca le eccezioni. Nel caso in cui qualcosa non va, basta stamparlo sullo schermo.
Quindi ho implementato il metodo launch. questo è chiamato da \Magento\Framework\App\Bootstrap::run. Questo runmetodo fa quasi la stessa cosa, indipendentemente dall'applicazione passata come parametro.
L'unica cosa che dipende dall'applicazione è questa riga:

$response = $application->launch();

Ciò significa che la chiamata \Magento\Framework\App\Bootstrap::runavvierà il Magento env (forse farà qualche altra cosa folle ... Non ho ancora controllato tutto) quindi chiama il launchmetodo dall'applicazione.
Ecco perché devi inserire tutto il tuo codice sporco all'interno di quel metodo.
Quindi le \Magento\Framework\App\Bootstrap::runchiamate $response->sendResponse();dove $responseè ciò launchche restituisce il metodo.
Ecco perché return $this->_response;è necessario. Restituisce solo una risposta vuota.

Ho ampliato la mia classe di app in \Magento\Framework\App\Httpmodo da avere già i parametri di richiesta e risposta (e altri), ma non puoi estendere nulla alla tua classe. Quindi è necessario copiare il costruttore dalla \Magento\Framework\App\Httpclasse. Forse aggiungi più parametri nel costruttore se ne hai bisogno.


2
Naturalmente la TestAppclasse avrebbe potuto essere definita nello stesso test.phpfile, ma non voglio renderla così sporca :)
Marius

Ho dovuto aggiungere parent::launch();come prima linea di launch()metodo perché mi stava dando un errore "Prefisso non impostato"
OSdave

@OSdave. Ha funzionato senza questo quando ho provato. Molto probabilmente qualcosa è cambiato nelle ultime versioni.
Marius

@Marius, avevo installato magento eliminando l'installazione rapida del server. E non hai bootstrap.php in app
er.irfankhan11,

1
@Butterfly Non è necessario includerlo nel controller personalizzato. Il file viene incluso in index.php prima di raggiungere il controller.
Marius

54

Per i test rapidi / brevi / sporchi, ho usato qualcosa del genere:

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
questo funziona. le altre risposte no.
ahnbizcad,

1
questo fa scattare HTTP 500 al mio fianco.
Max

Funziona ancora in 2.1.2. Ho dovuto modificare il percorso richiesto tho
simonthesorcerer

non ha funzionato per me
Sarfaraj Sipai

20

Sulla base della risposta di @ Marius, ho pensato a questo.

Funziona sia tramite la riga di comando che tramite il browser, che trovo utile.

Ecco uno script di esempio per eliminare a livello di codice la 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);

Quindi lo eseguo come php scripts/delete-category.php


2
funziona bene per il frontend, se voglio accedere al codice amministratore, allora mostra un errore di accesso o un problema di area, puoi dire come chiamare l'area di amministrazione
Pradeep Kumar

Quando si cerca di chiamare qualcosa, ottengo: Magento\Framework\Exception\LocalizedException: Area code is not set. Come posso impostarlo? Ho bisogno della fronenda.
Max

Non ho guardato molto a M2 da quando ho scritto questo codice, temo, i cambiamenti nel framework potrebbero averlo reso non valido o necessitante modifica, mi dispiace!
Luke Rodgers,

18

Come richiesto, un breve esempio di come è possibile scrivere un test (senza inserirlo nella struttura dell'estensione della cartella). Purtroppo questa è tutta la riga di comando e non per il consumo tramite un browser.

Crea il file

dev/tests/unit/quicktest.php

con

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

    }
}

quindi dalla directory dev/tests/unit/run phpunit quicktest.phpche eseguirà il tuo codice. Tutto funziona poiché il file dev/tests/unit/phpunit.xml.distviene caricato automaticamente e prepara l'ambiente.

In molti casi potresti dover fornire input al costruttore delle classi. Si prega di consultare i test esistenti sotto dev/tests/unit/testsuite/per ulteriori esempi di come potrebbe apparire, compresi gli oggetti beffardi.


1
Ho chiesto un parco giochi "sporco". Ne hai dato uno pulito :). Idea interessante. Ci proverò.
Marius

7
Trovo che le volte in cui avrei creato un test.php in passato, lo sforzo avrebbe potuto anche essere scritto in un test che avrebbe avuto un beneficio continuo.
Kristof a Fooman,

15

Ecco un modo migliore di collegarsi al sistema di test: utilizzare l'interfaccia della riga di comando di Magento 2.

Ciò significa che dovrai integrare il tuo codice sandbox in un modulo reale (o crearne uno per lo scopo), ma dovresti farlo comunque.

Una volta impostato il modulo , l' aggiunta di un comando è abbastanza semplice. Tutto ciò che serve è la classe e DI per registrarla.

1. {modulo} /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. {modulo} /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>');
    }
}

Esempio derivato da https://github.com/magento/magento2-samples/tree/master/sample-module-command - vedi qui per un modulo completo che incorpora questa funzionalità. Ci sono esempi meno banali inclusi.

Per convenzione, la tua classe di comando dovrebbe essere sempre dentro {module}/Console/Commande finire con Command.php.

Dopo aver aggiunto i due pezzi di codice (e la cache Magento arrossata, ecc), eseguire il comando per nome in SSH: php bin/magento example:greeting.

È possibile utilizzare l'iniezione di dipendenza in questo contesto, quindi è possibile eseguire qualsiasi codice si desideri all'interno execute().

Questa interfaccia è costruita sul componente Console di Symfony , quindi hai anche pieno accesso a tutta quella vasta gamma di funzionalità, incluse opzioni / argomenti , tabelle e barre di avanzamento molto facili .

In caso di problemi durante l'impostazione del comando o delle opzioni, in genere è possibile eseguire il comando 'list' per ottenere una migliore visibilità su ciò che non va: php bin/magento list

Godere.


Bello! con le barre di avanzamento di Symfony per script con grande esportazione. grazie
urbansurfers

13

La parte importante è il \Magento\Framework\App\Bootstrap::create

ma poiché il Bootstrap::init()metodo è privato e ci sono molte cose importanti, sono necessari metodi pubblici per chiamarlo.

Questo da un lato è il createApplication()seguente e il run()metodo, ma anche il metodo getDirList()e getObjectManager(), che entrambi non hanno bisogno di alcun argomento.

Quindi un'applicazione non è necessaria, gli svantaggi sono che l'errore Handler non è inizializzato.


6

Forse fuori tema, ma uso sempre il file del controller dell'indice dei contatti in Magento 1 per testare le cose (metodo IndexAction). È semplice come andare a example.com/contacts. Devi solo assicurarti di non eseguire il commit di tali modifiche;)

Sono sicuro che puoi fare qualcosa di simile in Magento 2. Ti risparmia di dover creare un nuovo file con il codice bootstrap.


1
Il cielo ti proibisce di dimenticare o di farlo in produzione! Si prega di non modificare il codice principale.
Ryan Hoerr,

@RyanH. Non succederà. Controllo della versione, build automatizzate, analisi del codice statico, revisione del codice peer, test di gestione temporanea / accettazione dell'utente / ecc. Ma sì, se non lo hai, c'è una possibilità che possa finire in produzione: P
Erfan,

1
È fantastico per te, ma la maggior parte delle persone che guardano qui non avranno questo tipo di controlli. Meglio insegnare (e fare) il modo giusto di fare le cose, sempre.
Ryan Hoerr,

5

Questa risposta è una leggera modifica alla risposta sopra di Marius

Perché in Magento 2.1 si è verificato l'errore come Area code not setquando si utilizza quel codice.So the intension of this answer is to fix that error on Magento 2.1

Quello che devi fare per correggere questo errore è definire l'area nel tuo test.php file. (vedi il file modificato di seguito).

<?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 il TestApp.phpfile rimarrà lo stesso.

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

}

Anche questo non funziona per me in 2.1.6, ottengoUncaught TypeError: Argument 2 passed to Magento\\Framework\\App\\Http::__construct() must be an instance of Magento\\Framework\\Event\\Manager, none given
Guerrilla

5

È possibile dirigere lo script sulla radice di Magento con l'aggiunta del codice seguente e il bootstrap verrà incluso. [Creare test.php nella cartella principale di Magento e includere il codice seguente]

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

Spero che questo possa essere utile.


2

Puoi eseguire lo script diretto dalla radice di Magento 2 usando il codice seguente. Crea un nuovo file nella directory principale di Magento 2 e aggiungi questo codice e successivamente aggiungi il tuo script nel file.

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

Ecco cosa ho fatto per portare l'inizializzazione di Magento nel mio script personalizzato al di fuori della directory di 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);

Questo è il modo raccomandato secondo i documenti di Magento. http://devdocs.magento.com/guides/v2.0/config-guide/bootstrap/magento-bootstrap.html

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.