ฉันจะ bootstrap Magento 2 ในสคริปต์ test.php ได้อย่างไร


93

ในวีโอไอพี 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::']]);ฉันควรสร้างโมเดลแอพของตัวเองหรือไม่
Marius

1
เขียนการทดสอบหน่วย
Kristof ที่ Fooman

2
@Fooman อย่าลังเลที่จะเขียนสิ่งนี้เป็นคำตอบ แต่โปรดยกตัวอย่าง ฉันเป็นคนใหม่สำหรับการทดสอบหน่วย
Marius

คำตอบ:


86

จากคำตอบของ @ Flyingmanaฉันได้ขุดเล็กน้อยและหาทางแก้ปัญหา มันทำงานได้ดีสำหรับฉัน
ก่อนอื่นวิธีแก้ปัญหาของฉันก็มีคำอธิบายบางอย่าง
ฉันได้สร้างไฟล์ที่เรียกว่าtest.phpในรากของอินสแตนซ์วีโอไอพีของฉัน

<?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จากคลาส bootstrap เป็นส่วนที่สำคัญที่สุด มันสร้างอินสแตนซ์ของคลาสแอปพลิเคชัน วิธีการที่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

@Marius ฉันได้ติดตั้ง magento โดยการปิดการติดตั้งเซิร์ฟเวอร์อย่างรวดเร็ว และไม่มี bootstrap.php ในแอป
er.irfankhan11

1
@Butterfly คุณไม่จำเป็นต้องรวมมันไว้ในคอนโทรลเลอร์แบบกำหนดเองของคุณ ไฟล์จะรวมอยู่ใน index.php ก่อนถึงตัวควบคุมของคุณ
Marius

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

ไม่ได้ผลสำหรับฉัน
Sarfaraj Sipai

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
ฉันถามหาสนามเด็กเล่น "สกปรก" คุณให้อันนี้สะอาด :) ความคิดที่น่าสนใจ ฉันจะลองดู
Marius

7
ฉันพบว่าเวลาที่ฉันสร้าง test.php ในอดีตความพยายามก็อาจรวมไปถึงการเขียนข้อสอบซึ่งจะมีประโยชน์อย่างต่อเนื่อง
Kristof ที่ Fooman

15

นี่เป็นวิธีที่ดีกว่าการเชื่อมต่อเข้ากับระบบทดสอบ: ใช้อินเตอร์เฟสบรรทัดคำสั่งของ Magento 2

นี่หมายความว่าคุณจะต้องรวมรหัส sandbox ของคุณเข้ากับโมดูลจริง (หรือสร้างรหัสเพื่อวัตถุประสงค์) แต่คุณควรทำเช่นนั้น

เมื่อคุณได้โมดูลของคุณตั้งค่า , การเพิ่มคำสั่งเป็นเรื่องง่ายสวย สิ่งที่คุณต้องมีคือชั้นเรียนและ 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/CommandCommand.php

เมื่อคุณได้เพิ่มทั้งสองบิตของรหัส (วีโอไอพีและแคชล้าง ฯลฯ ) ดำเนินการคำสั่งของคุณโดยใช้ชื่อใน php bin/magento example:greetingSSH:

execute()คุณสามารถใช้ฉีดอยู่ในบริบทนี้เพื่อให้คุณสามารถเรียกใช้รหัสที่คุณต้องการภายใน

อินเตอร์เฟซนี้ถูกสร้างขึ้นบน Symfony ขององค์ประกอบคอนโซลเพื่อให้คุณยังมีการเข้าถึงแบบเต็มไปยังทุกที่หลากหลายของการทำงานรวมทั้งตัวเลือก / ข้อโต้แย้ง , ตาราง , และง่ายมากแถบความคืบหน้า

หากคุณพบปัญหาในการตั้งค่าคำสั่งหรือตัวเลือกของคุณคุณสามารถเรียกใช้คำสั่ง 'รายการ' เพื่อให้มองเห็นสิ่งที่ผิดปกติได้ดีขึ้น: php bin/magento list

สนุก.


ดี! ด้วยแถบความคืบหน้าของ Symfony สำหรับสคริปต์ที่มีการส่งออกขนาดใหญ่ ขอบคุณ
urbansurfers

13

ส่วนที่สำคัญคือ \Magento\Framework\App\Bootstrap::create

แต่เนื่องจากBootstrap::init()วิธีการนี้เป็นแบบส่วนตัวและมีสิ่งที่สำคัญมากมายเกิดขึ้นจึงจำเป็นต้องมีวิธีการสาธารณะที่เรียกว่า

ที่ด้านหนึ่งcreateApplication()และต่อไปนี้run()วิธี แต่ยังgetDirList()และgetObjectManager()วิธีการซึ่งทั้งสองไม่จำเป็นต้องมีข้อโต้แย้ง

ดังนั้นไม่จำเป็นต้องมีแอปพลิเคชันข้อเสียคือตัวจัดการข้อผิดพลาดจะไม่ถูกเตรียมใช้งาน


6

อาจเป็นเรื่องนอกเรื่อง แต่ฉันมักจะใช้ไฟล์ตัวควบคุมดัชนีผู้ติดต่อในวีโอไอพี 1 เพื่อทดสอบสิ่งต่าง ๆ (วิธีการ IndexAction) ทำได้ง่ายเพียงไปที่ example.com/contacts คุณเพียงแค่ต้องแน่ใจว่าจะไม่ยอมรับการเปลี่ยนแปลงเหล่านั้น;)

ฉันแน่ใจว่าคุณสามารถทำสิ่งที่คล้ายกันใน Magento 2 สำรองคุณไม่ต้องสร้างไฟล์ใหม่ด้วยรหัส bootstrap


1
สวรรค์ห้ามคุณลืมหรือทำมันในการผลิต! กรุณาอย่าแก้ไขรหัสหลัก
Ryan Hoerr

@RyanH จะไม่เกิดขึ้น การควบคุมเวอร์ชัน, การสร้างอัตโนมัติ, การวิเคราะห์โค้ดแบบคงที่, การตรวจสอบโค้ดเพียร์, การทดสอบการยอมรับ / การทดสอบผู้ใช้ / ฯลฯ แต่ใช่ถ้าคุณไม่มีสิ่งนั้นมีโอกาสที่มันจะจบลงด้วยการผลิต: P
Erfan

1
นั่นยอดเยี่ยมสำหรับคุณ แต่คนส่วนใหญ่ที่ดูที่นี่จะไม่มีการควบคุมแบบนั้น ดีกว่าที่จะสอน (และทำ) วิธีที่ถูกต้องในการทำสิ่งต่าง ๆ เสมอ
Ryan Hoerr

5

คำตอบนี้เป็นการแก้ไขเล็กน้อยสำหรับคำตอบข้างต้นโดย Marius

เพราะใน 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
Guerrilla

5

คุณสามารถสั่งสคริปต์บนรูทวีโอไอพีด้วยการเพิ่มโค้ดด้านล่างและบูทสแตรปจะรวมอยู่ด้วย [สร้าง test.php ในโฟลเดอร์รูทวีโอไอพีและรวมรหัสด้านล่าง]

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

นี่เป็นวิธีที่แนะนำตามเอกสารของวีโอไอพี http://devdocs.magento.com/guides/v2.0/config-guide/bootstrap/magento-bootstrap.html

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.