Bagaimana saya bisa bootstrap Magento 2 dalam skrip test.php?

93

Di magento 1 saya bisa membuat file di mana saya hanya perlu membuat instance Mage_Core_Model_Appkelas dan kemudian saya bisa menambahkan kode "kotor" saya untuk tujuan pengujian.
Sesuatu seperti ini 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.

Kemudian saya bisa memanggil test.phpbrowser dan melihat apa yang saya lakukan.

Bagaimana saya bisa melakukan hal yang sama untuk Magento 2?

Marius
sumber
4
Bagaimana cara kerja cron magento 2? Mungkin Anda bisa menggunakan pendekatan yang sama?
Amasty
4
Ide bagus, tapi ... kode dari cron.php: $app = $bootstrap->createApplication('Magento\Framework\App\Cron', ['parameters' => ['group::']]);. Haruskah saya membuat model aplikasi saya sendiri?
Marius
1
tulis tes unit
Kristof di Fooman
2
@Fooman. Jangan ragu untuk menulis ini sebagai jawaban, tapi tolong berikan contoh. Saya agak baru dalam pengujian unit.
Marius

Jawaban:

86

Berdasarkan jawaban @ Flyingmana, saya melakukan sedikit penggalian dan menemukan solusi. Tampaknya bekerja untuk saya.
Pertama solusi saya, lalu beberapa penjelasan.
Saya telah membuat file bernama test.phproot dari contoh magento saya.

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

Kemudian saya membuat file yang disebut TestApp.phpdi tempat yang sama dengan konten ini.

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

}

Sekarang saya bisa memanggil test.phpbrowser dan segala sesuatu yang ditempatkan di TestApp :: launch () akan dieksekusi.

Sekarang, mengapa ini berhasil:
Metode createApplicationdari kelas bootstrap adalah bagian yang paling penting. Itu menciptakan turunan dari suatu kelas aplikasi. Metode ini createApplicationmengharapkan implementasi dari \Magento\Framework\AppInterfaceyang berisi 2 metode.
Jadi saya membuat kelas saya sendiri dalam TestAppmengimplementasikan antarmuka itu. Saya membuat metode catchExceptionkembali falseselalu karena saya tidak ingin aplikasi saya menangani pengecualian. Jika ada sesuatu yang salah, cetak saja di layar.
Kemudian saya menerapkan metode ini launch. ini disebut oleh \Magento\Framework\App\Bootstrap::run. runMetode ini melakukan hal yang hampir sama tidak peduli apa aplikasi dilewatkan sebagai parameter.
Satu-satunya hal yang tergantung pada aplikasi adalah baris ini:

$response = $application->launch();

Ini berarti bahwa panggilan \Magento\Framework\App\Bootstrap::runakan init Magento env (mungkin melakukan beberapa hal gila lainnya ... Saya belum memeriksa semuanya) kemudian memanggil launchmetode dari aplikasi.
Itu sebabnya Anda harus memasukkan semua kode kotor Anda ke dalam metode itu.
Kemudian \Magento\Framework\App\Bootstrap::runpanggilan $response->sendResponse();mana $responseadalah apa yang launchkembali metode.
Karena itulah return $this->_response;diperlukan. Itu hanya mengembalikan respons kosong.

Saya membuat kelas aplikasi saya diperluas \Magento\Framework\App\Httpsehingga saya sudah memiliki parameter permintaan dan respons (dan lainnya), tetapi Anda dapat membuat kelas Anda tidak memperpanjang apa pun. Maka Anda perlu menyalin konstruktor dari \Magento\Framework\App\Httpkelas. Mungkin menambahkan lebih banyak parameter di konstruktor jika Anda membutuhkannya.

Marius
sumber
2
Tentu saja TestAppkelasnya dapat didefinisikan dalam test.phpfile yang sama , tetapi saya tidak ingin membuatnya sekotor itu :)
Marius
Saya harus menambahkan parent::launch();sebagai baris pertama launch()metode karena memberi saya kesalahan "Kode area tidak disetel"
OSdave
@ OSdave. Itu bekerja tanpa itu ketika saya diuji. Kemungkinan besar ada yang berubah di versi terbaru.
Marius
@Marius, saya telah menginstal magento dengan memasang instal cepat server. Dan tidak punya aplikasi bootstrap.php
er.irfankhan11
1
@ Butterfly Anda tidak perlu memasukkannya ke pengontrol khusus Anda. File dimasukkan dalam index.php sebelum mencapai controller Anda.
Marius
54

Untuk tes cepat / pendek / kotor, saya menggunakan sesuatu seperti ini:

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
sumber
4
ini bekerja. jawaban yang lain tidak.
ahnbizcad
1
ini memicu HTTP 500 di sisiku.
Maks
Masih berfungsi di 2.1.2. Saya harus memodifikasi memerlukan-jalan tho
simonthesorcerer
tidak bekerja untuk saya
Sarfaraj Sipai
20

Berdasarkan jawaban @ Marius saya datang dengan ini.

Ini berfungsi baik melalui baris perintah maupun browser, yang menurut saya berguna.

Berikut ini contoh skrip untuk menghapus kategori secara pemrograman.

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

Lalu saya jalankan saja seperti php scripts/delete-category.php

Luke Rodgers
sumber
2
berfungsi dengan baik untuk frontend, jika saya ingin mengakses kode admin maka itu menunjukkan kesalahan akses atau masalah area, dapatkah Anda memberi tahu cara memanggil area admin
Pradeep Kumar
Ketika mencoba untuk menyebut sesuatu, saya mendapatkan: Magento\Framework\Exception\LocalizedException: Area code is not set. Bagaimana saya bisa mengaturnya? Saya butuh fronend.
Maks
Saya tidak melihat M2 banyak sejak saya menulis kode ini, saya khawatir, perubahan dalam kerangka kerja mungkin membuatnya tidak valid atau perlu diubah, maaf!
Luke Rodgers
18

Seperti yang diminta, contoh yang sangat singkat tentang bagaimana Anda dapat menulis tes (tanpa menempatkannya dalam struktur ekstensi folder Anda). Sayangnya ini semua baris perintah dan bukan untuk konsumsi melalui browser.

Buat file

dev/tests/unit/quicktest.php

dengan

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

    }
}

lalu dari direktori dev/tests/unit/ jalankanphpunit quicktest.php yang akan menjalankan kode Anda. Ini semua berfungsi sejak file dev/tests/unit/phpunit.xml.distdimuat secara otomatis dan menyiapkan lingkungan.

Dalam banyak kasus, Anda mungkin harus memberikan input ke konstruktor kelas. Silakan lihat tes yang ada di bawah dev/tests/unit/testsuite/untuk contoh lebih lanjut tentang bagaimana ini bisa terlihat, termasuk benda mengejek.

Kristof di Fooman
sumber
1
Saya meminta taman bermain "kotor". Anda memberi di sini yang bersih :). Ide yang menarik. Saya akan mencobanya.
Marius
7
Saya menemukan bahwa saat-saat saya akan membuat test.php di masa lalu upaya mungkin juga harus menulis tes yang akan memiliki manfaat yang berkelanjutan.
Kristof di Fooman
15

Berikut cara yang lebih baik daripada menghubungkan ke sistem pengujian: Gunakan antarmuka baris perintah Magento 2.

Ini berarti Anda harus mengintegrasikan kode kotak pasir Anda ke dalam modul yang sebenarnya (atau membuatnya untuk tujuan itu), tetapi Anda tetap harus melakukannya.

Setelah Anda mengatur modul , menambahkan perintah cukup mudah. Yang Anda butuhkan hanyalah kelas, dan DI untuk mendaftarkannya.

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

Contoh berasal dari https://github.com/magento/magento2-samples/tree/master/sample-module-command - lihat di sana untuk modul lengkap yang menggabungkan fungsi ini. Ada contoh-contoh yang kurang sepele disertakan.

Dengan konvensi, kelas perintah Anda harus selalu berada {module}/Console/Command, dan diakhiri dengan Command.php.

Setelah Anda menambahkan dua bit kode (dan cache Magento memerah, dll.), Jalankan perintah Anda dengan nama di SSH: php bin/magento example:greeting .

Anda bisa menggunakan injeksi ketergantungan dalam konteks ini, sehingga Anda bisa menjalankan kode apa pun yang Anda inginkan execute() .

Antarmuka ini dibangun di atas komponen Konsol Symfony , sehingga Anda juga memiliki akses penuh ke semua fungsionalitas yang luas itu, termasuk opsi / argumen , tabel , dan bilah kemajuan yang sangat mudah .

Jika Anda mengalami masalah dalam mengatur perintah atau opsi, Anda biasanya dapat menjalankan perintah 'daftar' untuk mendapatkan visibilitas yang lebih baik mengenai apa yang salah: php bin/magento list

Nikmati.

Ryan Hoerr
sumber
Bagus! dengan progress bar Symfony untuk skrip dengan ekspor besar. terima kasih
urbansurfers
13

Bagian yang penting adalah \Magento\Framework\App\Bootstrap::create

tetapi karena Bootstrap::init()metode ini bersifat pribadi, dan ada banyak hal penting yang terjadi, metode publik yang memanggilnya diperlukan.

Itu di satu sisi createApplication()dan mengikuti run()metode, tetapi juga metode getDirList()dan getObjectManager(), yang keduanya tidak perlu argumen.

Jadi Aplikasi tidak diperlukan, kelemahannya, bahwa Handler kesalahan tidak diinisialisasi.

Flyingmana
sumber
6

Mungkin di luar topik, tetapi saya selalu menggunakan file pengontrol indeks Kontak di Magento 1 untuk menguji berbagai hal (metode IndexAction). Sesederhana pergi ke example.com/contacts. Anda hanya perlu memastikan untuk tidak melakukan perubahan itu;)

Saya yakin Anda dapat melakukan hal serupa di Magento 2. Menghindarkan Anda dari keharusan membuat file baru dengan kode bootstrap.

Erfan
sumber
1
Surga melarang Anda lupa, atau melakukannya pada produksi! Tolong jangan modifikasi kode inti.
Ryan Hoerr
@RyanH. Tidak akan terjadi Kontrol versi, build otomatis, analisis kode statis, tinjauan kode rekan, pengujian staging / penerimaan pengguna / dll. Tapi ya, jika Anda tidak memiliki itu, ada kemungkinan itu bisa berakhir pada produksi: P
Erfan
1
Itu bagus untuk Anda, tetapi kebanyakan orang yang mencari di sini tidak akan memiliki kontrol semacam itu. Lebih baik mengajar (dan melakukan) cara yang benar dalam melakukan sesuatu, selalu.
Ryan Hoerr
5

Jawaban ini adalah sedikit modifikasi dari jawaban di atas oleh Marius

Karena di Magento 2.1 mendapat kesalahan seperti Area code not setketika menggunakan kode itu.So the intension of this answer is to fix that error on Magento 2.1

Yang harus Anda lakukan untuk memperbaiki kesalahan ini adalah menentukan area di Anda test.php file. (lihat file yang dimodifikasi di bawah).

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

Dan TestApp.phpfile akan tetap sama.

<?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
sumber
Ini juga tidak bekerja untuk saya di 2.1.6, saya mengertiUncaught TypeError: Argument 2 passed to Magento\\Framework\\App\\Http::__construct() must be an instance of Magento\\Framework\\Event\\Manager, none given
Gerilya
5

Anda dapat mengarahkan skrip pada root magento dengan menambahkan kode di bawah ini dan bootstrap akan disertakan .. [Buat test.php pada folder root magento dan sertakan kode di bawah ini]

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

Semoga ini bisa membantu.

Yogesh
sumber
2

Anda dapat menjalankan skrip langsung dari root Magento 2 menggunakan kode di bawah ini. Buat file baru di direktori root Magento 2 dan tambahkan kode ini dan setelah ini tambahkan skrip Anda di 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');
Evince Development
sumber
1

Inilah yang saya lakukan untuk membawa inisialisasi Magento ke dalam skrip khusus saya di luar direktori 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);

Ini adalah cara yang disarankan menurut dokumen Magento. http://devdocs.magento.com/guides/v2.0/config-guide/bootstrap/magento-bootstrap.html

MagentoMan
sumber