?> magento 2 Archives - Addeos

Category Archive magento 2

Enhancing Magento 2 JavaScript Modules with Mixins: A Guide

Introduction:

Magento 2 provides a robust and extensible architecture that allows developers to customize and extend functionality easily. One powerful feature for extending JavaScript modules is the use of mixins. In this article, we will explore how to add a mixin to an existing JavaScript module in Magento 2, drawing parallels to the familiar concept of writing PHP plugins for adding specific treatments after, before or around existing functions.

Understanding Mixins in Magento 2:

Mixins in Magento 2 JavaScript development enable developers to add or override methods in existing classes. This approach promotes code reusability and allows for seamless customization without modifying the core codebase. Just as PHP plugins can extend and modify the behavior of existing PHP classes, mixins empower JavaScript developers to enhance the functionality of JavaScript modules in a clean and modular way.

Prerequisites:

Before diving into the mixin implementation, ensure you have a basic understanding of Magento 2 JavaScript development and are familiar with the structure of the module you intend to extend.

Step 1: Create Your Mixin File:

In your custom module, create a JavaScript mixin file. This file should follow the naming convention "vendor/namespace/view/frontend/web/js/mixin-name-mixin.js". This file will contain the code for your mixin.

Let's say my module vendor and namespace is Addeos_MyModule.

And I want to create a mixin for this core file : vendor/magento/module-checkout/view/frontend/web/js/model/shipping-save-processor/default.js

I am going to create this file:
app/code/Addeos/MyModule/view/frontend/web/js/checkout/model/shipping-save-processor/default-mixin.js
I respect the same folder hierachy.

Here is the content of that file:

define(['jquery', 'mage/utils/wrapper'], function ($, wrapper) {
    'use strict';

    return function (defaultShippingSaveProcessor) {
        defaultShippingSaveProcessor.saveShippingInformation = wrapper.wrapSuper(
            defaultShippingSaveProcessor.saveShippingInformation,
            function () {
                console.log('here, I can do want I want.');
                return this._super();
            }
        );
        return defaultShippingSaveProcessor;
    };
});

Step 2: Apply the Mixin to the Target Module:

To apply the mixin to the target module, create a requirejs-config.js file in your module's view/frontend directory.

var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/model/shipping-save-processor/default': {
                'Addeos_MyModule/js/checkout/model/shipping-save-processor/default-mixin': true,
            },
        },
    },
};

Conclusion:

Adding a mixin to an existing JavaScript module in Magento 2 is a powerful way to customize functionality without modifying core code. Drawing parallels to PHP plugins, mixins offer a clean and modular approach to extending JavaScript modules, ensuring maintainability and ease of future upgrades. Incorporate this technique into your Magento 2 development toolkit to create flexible and scalable solutions.

Magento 2 Security Best Practices

Safeguarding Your E-Commerce Store

Introduction

In the ever-evolving landscape of e-commerce, ensuring the security of your Magento 2 store is paramount. This article will guide you through essential security best practices, helping you protect your online business and instill confidence in your customers.

1. Keep Magento 2 Updated

Regularly updating your Magento 2 installation is the first line of defense against potential vulnerabilities. Magento frequently releases security patches and updates to address known issues. Make it a practice to stay current with the latest versions.

Check the published versions of Adobe Commerce here. It contains the release dates of each minor, patch, and security release, as well as the end of support dates.

Take a look at the lifecycle policy here.

2. Secure Admin Access

  • Unique Admin URL: Change the default admin URL to reduce the risk of brute-force attacks.
  • Strong Passwords: Enforce strong password policies for admin accounts, including a combination of letters, numbers, and special characters.

3. SSL Implementation

Ensure that your Magento 2 store uses Secure Socket Layer (SSL) to encrypt data transmission. This is crucial for securing sensitive information such as customer details and payment transactions.

4. Regular Security Audits

Performing regular security audits is essential to identify vulnerabilities and address them promptly. Consider employing third-party security tools or services to conduct thorough assessments.

5. File Permissions and Ownership

Set appropriate file permissions and ownership for your Magento 2 installation. Limit access to critical files and directories, reducing the likelihood of unauthorized access or modification.

6. Web Application Firewall (WAF)

Implement a Web Application Firewall to protect your store from common web threats. A WAF can filter and monitor HTTP traffic between a web application and the Internet, providing an additional layer of security.

7. Two-Factor Authentication (2FA)

Enforce Two-Factor Authentication for admin accounts to add an extra layer of security. This ensures that even if login credentials are compromised, an additional verification step is required.

8. Regular Backups

Frequently back up your Magento 2 store, including databases and media files. In the event of a security breach or data loss, having recent backups can significantly reduce downtime and data loss.

9. Extension Security

Carefully vet and only install reputable extensions from trusted sources. Regularly update installed extensions to benefit from security patches and improvements.

10. Educate Your Team

Security is a collective effort. Educate your team members on security best practices, including recognizing phishing attempts, safeguarding login credentials, and reporting suspicious activities promptly.

Conclusion

By implementing these Magento 2 security best practices, you fortify your e-commerce store against potential threats and build a foundation of trust with your customers. Stay vigilant, stay secure, and keep your online business thriving in a secure digital environment.

Exciting News: My First Magento Extension is Now Live on the Marketplace!

I am thrilled to share some exciting news with you! Today marks a significant milestone for me as I proudly announce the deployment of my very first extension on the Adobe Magento Marketplace.

This extension empowers you to anonymize your database with a new CLI command, ensuring that sensitive customer privacy details are obfuscated for enhanced security and GDPR compliance.

I wrote an article in the past. Here it is : https://www.addeos.com/magento-2-database-anonymization-module

The module is still available on packagist here : https://packagist.org/packages/addeos/anonymize

And you can find it on the Adobe Magento Marketplace here: https://commercemarketplace.adobe.com/addeos-anonymize.html

Magento 2 – Adding Custom Configuration – Quick Tutorial

Welcome to our comprehensive Magento 2 custom configurations guide.

In this tutorial, we'll walk you through the essential steps to seamlessly integrate and manage personalized settings for your e-commerce store. Unlock the full potential of your Magento 2 experience with our expert insights and easy-to-follow instructions.

Follow these steps to integrate your custom configuration effortlessly:

Step 1: Create your custom module

Develop a custom module if you haven't already. This typically involves setting up the necessary files and directories. Check this post for creating your first module : https://www.addeos.com/magento-2-creating-a-magento-module-a-beginners-guide

Step 2: Create system.xml file

Inside your module's etc folder, create a system.xml file. This file will define your custom configuration fields.

Step 3: Define configuration fields

Within system.xml, define your custom configuration fields using XML tags like <section>, <group>, and <field>. Specify field types, labels, and other attributes.

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="custom_section" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Custom Section</label>
            <tab>general</tab>
            <resource>Magento_Config::config</resource>
            <group id="custom_group" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Custom Group</label>
                <field id="custom_field" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Custom Field</label>
                    <comment>Some description for your custom field.</comment>
                    <validate>validate-number</validate>
                </field>
            </group>
        </section>
    </system>
</config>

Step 4: Run setup upgrade command

After defining your configuration, run the setup upgrade command to apply changes:

php bin/magento setup:upgrade

Step 5: Access Configuration in the Admin Panel

Navigate to Stores > Configuration in the Admin Panel. Your custom configuration section and fields should be visible for modification.

Congratulations! You've successfully added custom configuration settings to Magento 2 through your custom module. Adjust these steps according to your specific requirements.

Magento 2 – Creating a Magento Module: A Beginner’s Guide

Magento's modular architecture empowers developers to customize and extend functionality. In this tutorial, we'll guide you through the essential steps to create a basic Magento module.

Here is our Magento module creation guide.

Step 1: Set Up Your Development Environment

Ensure you have a Magento instance for development. Familiarize yourself with Magento's file structure and basic module concepts.
If you need to set it up quickly check Mark Shust solution : https://github.com/markshust/docker-magento

Step 2: Create the Module Directory Structure

Navigate to the app/code directory and create the directory structure for your module. For instance, if your module is named MyModule, the path should be app/code/Vendor/MyModule.

Step 3: Define Module Configuration

Inside your module directory, create a registration.php file to register your module:

<?php 
\Magento\Framework\Component\ComponentRegistrar::register( 
    \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Vendor_MyModule', __DIR__ 
);

Step 4: Create Module Configuration File

Create a module.xml file in app/code/Vendor/MyModule/etc:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_MyModule" setup_version="1.0.0"/>
</config>

Step 5: Enable the Module

Run the following command in the Magento root directory to enable your module:

php bin/magento module:enable Vendor_MyModule

Step 6: Verify Your Module

Check the enabled modules by running:

php bin/magento module:status

Your module, Vendor_MyModule, should appear in the list.

Congratulations! You've created a minimal Magento module using our Magento module creation guide. While this module doesn't include a custom controller, you can build upon this foundation to add more features and functionality as needed. Happy coding!

Magento 2 – New logging function compatible with version 2.4.3

Magento 2 debug log 

In a previous article, I explained how I add temporary debug logs with magento 2 in my code while coding. It can be very useful, it saves me time and does not require installing additional tools. You just have to remember that this solution is used temporarily and must not be committed in the code.

The complete post is here : https://www.addeos.com/magento-2-adding…n-when-debugging

Please the read the post again to understand how to add this as a live template in phpStorm which is the IDE I am using all the time.

With Magento version 2.4.3, I now use the following function which is still working.

The logging function has to be updated to :

<?php
private function log($str)
{
    $str = 'CLASS : ' . str_pad(__CLASS__, 50, ' ')
        . ' - LINE : ' . debug_backtrace()[0]['line']
        . ' - FUNCTION : ' . str_pad(debug_backtrace()[1]['function'], 15, ' ')
        . ' - STR : ' . $str;
    $filesystem = new \Magento\Framework\Filesystem\Driver\File();
    $handler = new \Magento\Framework\Logger\Handler\Base($filesystem, 'var/log/', 'custom_logs.log');
    $loggerHandlers = ['debug' => $handler];
    $logger = new \Monolog\Logger('customlogger', $loggerHandlers);
    $logger->addDebug($str);
}

Magento 2 – Development environment

I just made available on github a project that allows you to install a local development environment with a working version of magento within it.

Here is the url of its repo : https://github.com/berliozd/magento-env

Description

After cloning the project, you will have to execute two commands.

The first one should be executed from your local machine and will allow you to build the necessary docker containers which are described in the docker-compose.yml file.

6 docker containers will be needed:

  • php
  • nginx
  • mysql
  • redis
  • rabbitmq
  • elasticsearch

The second command must be executed from the php container.
It will create the magento project using composer. The latest version of magento (aka Adobe commerce) will be installed. At this time the latest version is 2.4.
As the magento source code is downloaded from the repository https://repo.magento.com/, you will be asked for access keys. To obtain these keys, you must have a magento developper account and access the magento marketplace at this page https://marketplace.magento.com/customer/accessKeys/.
Once the magento project is created, the script will continue with the magento installation itself.

Once this is finished, you will be able to access your new magento instance.

Step by step guide

  • clone the git projet
    git clone git@github.com:berliozd/magento-env.git .
  • run pre.sh from your local machine et the root of the git project
    docker/pre.sh
  • run bash inside the PHP container
    docker-compose -f docker-compose.mac.yml run php bash
  • run init.sh from the PHP container
    docker/php/init.sh

That's all.

You can now use that development environment to test some tutorial I wrote : https://www.addeos.com/magento-2-tutorial-adding-a-new-model-part-1-creating-the-db-table

Conclusion

It is with this solution that I am currently working on my Magento projects.
It allows me to quickly install a new instance of Magento.
I would be interested to know what solutions are used by magento developers around the world. Feel free to share in the comments how you work and how you configure your work environment.

Magento 2 – Tutorial : new model – Part 2 – creating the PHP classes

Introduction

This article is the 2nd part of a series in which I go through the procedure to create a new model in magento 2, with its own magento 2 admin grid in the Back Office.

You can go back to the first part if you wish.

As a preamble, I have to say that this tutorial series has been developed and tested with Magento version 2.4.2.

I remind you that for the purpose of the article I am going to create a "provider" model and that our code will be contained in an "Addeos_ModelTutorial" module.

In the first part I have explained how to create the DB table using declarative XML. In this second part, I will create the PHP classes :

  • the API data interface
  • the model that implements the interface
  • the resource model
  • the collection resource model

Creating the API data interface

If we want to respect the concept of service contracts which is a good thing and recommanded by Magento, I need to create some PHP interfaces first. Here is some more details about service contracts.

By convention, these PHP interfaces have to be in an Api folder and a Data folder for data interfaces.

The first interface to create is the ProviderInterface : Api/Data/ProviderInterface.php.

Here is the code :

<?php

declare(strict_types=1);

namespace Addeos\ModelTutorial\Api\Data;

/**
 * @api
 */
interface ProviderInterface
{
    const ENTITY_ID = 'entity_id';
    const CREATED_AT = 'created_at';
    const UPDATED_AT = 'updated_at';
    const NAME = 'name';
    const DESCRIPTION = 'description';

    public function getEntityId(): ?int;

    public function setEntityId(int $entityId);

    public function getName(): ?string;

    public function setName(string $name);

    public function getDescription(): ?string;

    public function setDescription(string $description);

    public function getCreatedAt(): ?string;

    public function setCreatedAt(string $createdAt);

    public function getUpdatedAt(): ?string;

    public function setUpdatedAt(string $updatedAt);

}

Creating the PHP model

I then need to create the PHP model that will implement the interface we have just created. This PHP class will extend \Magento\Framework\Model\AbstractModel.

Here is the code :

<?php
declare(strict_types=1);

namespace Addeos\ModelTutorial\Model;

use Addeos\ModelTutorial\Api\Data\ProviderInterface;
use Magento\Framework\Model\AbstractModel;

class Provider extends AbstractModel implements ProviderInterface
{

    public function getName(): ?string
    {
        return $this->getData(self::NAME);
    }

    public function setName(string $name)
    {
        $this->setData(self::NAME, $name);
    }

    public function getDescription(): ?string
    {
        return $this->getData(self::DESCRIPTION);
    }

    public function setDescription(string $description)
    {
        $this->setData(self::DESCRIPTION, $description);
    }

    public function getCreatedAt(): ?string
    {
        return $this->getData(self::CREATED_AT);
    }

    public function setCreatedAt(string $createdAt)
    {
        $this->setData(self::CREATED_AT, $createdAt);
    }

    public function getUpdatedAt(): ?string
    {
        return $this->getData(self::CREATED_AT);
    }

    public function setUpdatedAt(string $updatedAt)
    {
        $this->setData(self::UPDATED_AT, $updatedAt);
    }
}

Creating the resource model

I now need to create resource model.

The file is Model/ResourceModel/Provider.php.

Here is the code :

<?php
declare(strict_types=1);

namespace Addeos\ModelTutorial\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

class Provider extends AbstractDb
{
    protected function _construct()
    {
        $this->_init('addeos_provider', 'entity_id');
    }
}

Creating the collection resource model

I now need to create the provider collection resource model.

The file is ModelResourceModel/Provider/Collection.php.

Her is the code :

<?php

namespace Addeos\ModelTutorial\Model\ResourceModel\Provider;

use Addeos\ModelTutorial\Model\Provider;
use Addeos\ModelTutorial\Model\ResourceModel\Provider as ProviderResourceModel;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;

class Collection extends AbstractCollection
{
    protected $_idFieldName = 'entity_id';
    protected $_eventPrefix = 'addeos_provider_collection';
    protected $_eventObject = 'addeos_provider_collection';

    protected function _construct()
    {
        $this->_init(Provider::class, ProviderResourceModel::class);
    }
}

And that's all. From that point we have all the necessary classes for now.

In next post, I will explain now how to create the grid in Back Office to visualize the data of our new table.

Magento 2 – Tutorial : adding a new model – Part 1 – creating the DB table

Introduction

Here is the first post of a series during which I will got through all the procedure to create a new model in magento 2, with its own grid in the Back Office.

Here are the steps we will have to through :

  1. Creating the DB table
  2. Creating the API data object
  3. Creating the model
  4. Creating the resource model
  5. Creating the collection resource model
  6. Creating the grid

I will cover all these steps in a series of posts.

As a preamble, I have to say that this tutorial series has been developed and tested with Magento version 2.4.2.

For the purpose of the series, we will create a simple model for a "provider" entity with the following columns :

  • id : the provider unique identifier
  • created_at : the date the provider was created
  • updated_at : the date the provider was updated
  • name : the provider name
  • description : the provider description

We will assume that our code will be held in a module Addeos_ModelTutorial.

Creating the table in DB

Rather than using InstallSchema.php file we can now use XML declarative schema definition for creating new tables, or even updating existing tables. This is available since Magento version 2.3.

We will create a db_schema.xml file.

He is the content of the file:

<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="addeos_provider" resource="default" engine="innodb" comment="Porviders">
        <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="true"
                comment="Entity ID"/>
        <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP"
                comment="Created At"/>
        <column xsi:type="timestamp" name="updated_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP"
                comment="Updated At"/>
        <column xsi:type="varchar" name="name" nullable="true" comment="Name" length="50"/>
        <column xsi:type="varchar" name="description" nullable="true" comment="Description" length="50"/>
        <constraint xsi:type="primary" referenceId="PRIMARY">
            <column name="entity_id"/>
        </constraint>
        <index referenceId="ADDEOS_PROVIDER_ENTITY_ID" indexType="btree">
            <column name="entity_id"/>
        </index>
    </table>
</schema>

More information on declarative schema can be found here.

Once this file is in place, you will simply need to run a setup upgrade command from your magento installation root directory :

bin/magento setup:upgrade --keep-generated

Once this is done, your new table will be present in DB.

And this is all for that first step.

In the next post I explain the different PHP classes we need to create.

Magento 2 – Quick tip : how to quickly execute any magento 2 code in command line

The solution I am about to present allows you to execute any magento 2 code from command line.

While developing on magento 2, the code you are working on can be difficult to access and test because it sometimes needs to be executed in a specific context or after having clicked several times to access a specific page. That's why you often need to test some code without being obliged to go all the way.

I simply use a simple PHP script that can be executed in command line.

Here is a the empty structure of this script :

<?php

require '/var/www/html/app/bootstrap.php';

use Magento\Framework\App\Bootstrap;

class tmp
{

    public function __construct()
    {
        $params = $_SERVER;
        $bootstrap = Bootstrap::create(BP, $params);
        $obj = $bootstrap->getObjectManager();
    }

    public function execute()
    {

    }
}

$script = new tmp();
$script->execute();

I store this file in my magento folder in a __utils folder, name it tmp.ph for example and execute it like that :

I won't go into details but I run the previous command in the PHP docker container where magento runs. Prior to running the commande, I have to cd in the magento root.

This can be very helpful because it allows you to execute some code out of any context and really isolate a specific process and identify why potential bugs occurs, but also it can handy if you want to :

  • test and debug patch code before writing a proper patch
  • execute SQL statement
  • verify collections contents
  • automatise test data creation (product, sales rules or any other entities)
  • check some third party API calls

Here is a full example.

<?php

require '/var/www/html/app/bootstrap.php';

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Customer\Model\ResourceModel\CustomerRepository;
use Magento\Framework\App\Bootstrap;
use Magento\Framework\App\State;
use Magento\Framework\DataObject;
use Magento\Framework\ObjectManagerInterface;
use Magento\Quote\Api\CartManagementInterface;
use Magento\Quote\Model\Quote;
use Magento\Quote\Model\ResourceModel\Quote\Payment;
use Magento\QuoteGraphQl\Model\Cart\AssignBillingAddressToCart;
use Magento\QuoteGraphQl\Model\Cart\AssignShippingAddressToCart;
use Magento\QuoteGraphQl\Model\Cart\AssignShippingMethodToCart;
use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer;
use Magento\QuoteGraphQl\Model\Cart\GetCartForUser;
use Magento\QuoteGraphQl\Model\Cart\QuoteAddressFactory;
use Magento\Store\Model\App\Emulation;
use Magento\Quote\Model\QuoteManagement;

class createOrder
{
    private State $state;
    private ObjectManagerInterface $objectManager;
    private CustomerRepository $customerRepository;
    private CreateEmptyCartForCustomer $createEmptyCartforCustomer;
    private GetCartForUser $getCartForUser;
    private Emulation $emulation;
    private ProductRepositoryInterface $productRepository;
    private QuoteManagement $quoteManagement;
    private QuoteAddressFactory $quoteAddressFactory;
    private AssignShippingAddressToCart $assignShippingAddressToCart;
    private AssignBillingAddressToCart $assignBillingAddressToCart;
    private AssignShippingMethodToCart $assignShippingMethodToCart;
    private Payment $resourcePayment;
    private \Magento\Quote\Model\ResourceModel\Quote $quoteResourceModel;
    private CartManagementInterface $cartManagement;

    public function __construct()
    {
        $params = $_SERVER;
        $bootstrap = Bootstrap::create(BP, $params);
        $obj = $bootstrap->getObjectManager();
        $this->state = $obj->get(State::class);
        $this->state->setAreaCode('frontend');

        $obj = $bootstrap->getObjectManager();
        $this->objectManager = $obj;
        $this->customerRepository = $obj->get(CustomerRepository::class);
        $this->createEmptyCartforCustomer = $obj->get(CreateEmptyCartForCustomer::class);
        $this->getCartForUser = $obj->get(GetCartForUser::class);
        $this->emulation = $obj->get(Emulation::class);
        $this->emulation->startEnvironmentEmulation(3);
        $this->productRepository = $obj->get(ProductRepositoryInterface::class);
        $this->quoteManagement = $obj->get(QuoteManagement::class);
        $this->quoteAddressFactory = $obj->get(QuoteAddressFactory::class);
        $this->assignBillingAddressToCart = $obj->get(AssignBillingAddressToCart::class);
        $this->assignShippingAddressToCart = $obj->get(AssignShippingAddressToCart::class);
        $this->assignShippingMethodToCart = $obj->get(AssignShippingMethodToCart::class);
        $this->resourcePayment = $obj->get(Payment::class);
        $this->quoteResourceModel = $obj->get(\Magento\Quote\Model\ResourceModel\Quote::class);
        $this->cartManagement = $obj->get(CartManagementInterface::class);
    }

    public function execute()
    {
        $customer = $this->customerRepository->get('customer@example.com', 3);

        // Create empty cart
        $maskedCartId = $this->createEmptyCartforCustomer->execute($customer->getId());

        // Get cart
        /** @var Quote $cart */
        $cart = $this->getCartForUser->execute($maskedCartId, $customer->getId(), 3);

        // Add bundle to cart
        $bundleRequest = ['bundle_option' => [578 => 1122]];
        $bundle = $this->productRepository->get('bundle_sku');
        $cart->addProduct($bundle, new DataObject($bundleRequest));

        $this->quoteResourceModel->save($cart);
        $quote = $this->cartManagement->getCartForCustomer($customer->getId());

        $addressInputBilling = [
            'country_code' => "FR",
            'street' => ["55 billing street"],
            'telephone' => "0123456789",
            'postcode' => "75000",
            'city' => "Paris",
            'firstname' => "Customer",
            'lastname' => "Test",
            'save_in_address_book' => false
        ];
        $billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInputBilling);
        $this->assignBillingAddressToCart->execute($quote, $billingAddress, false);

        // Set shipping address
        $addressInputShipping = [
            'country_code' => "FR",
            'street' => ["55 shipping street"],
            'telephone' => "01223456789",
            'postcode' => "75001",
            'city' => "PAris",
            'firstname' => "Customer",
            'lastname' => "Test",
            'save_in_address_book' => false
        ];
        $shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInputShipping);
        $this->assignShippingAddressToCart->execute($quote, $shippingAddress);
        // Set shipping method
        $this->assignShippingMethodToCart->execute($quote, $shippingAddress, 'my_carrier_code', 'method_code');

        $payment = $quote->getPayment();
        $payment->setMethod('free');
        $this->resourcePayment->save($payment);

        $order = $this->quoteManagement->submit($quote);
    }
}

$script = new createOrder();
$script->execute();

In that example, we can see that we use the object manager in the constructor for creating several instances of specific repositories, or services that we later use in the execute method.

We simply programmatically create an order in that example using all the different services that it requires.

I recall that this script is only for testing and debugging your code. Directly using the object manager is not a good practice and we are using it here because dependency injection cannot be used.

That's all.