How to access the database layer in Shopware?

Viewed 1591

I have a custom class in Shopware that is not extending any other class in the CMS.

I want to have access to the database layer without using the container->get() or the DI service.

I don't know how to get the container to work in my class.

<?php declare(strict_types=1);

namespace Author\PriceDiscountPlugin\Handlers;

use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\CartBehavior;
use Shopware\Core\Checkout\Cart\CartProcessorInterface;
use Shopware\Core\Checkout\Cart\LineItem\CartDataCollection;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
use Shopware\Core\Checkout\Cart\Price\PercentagePriceCalculator;
use Shopware\Core\Checkout\Cart\Price\Struct\PercentagePriceDefinition;
use Shopware\Core\Checkout\Cart\Rule\LineItemRule;
use Shopware\Core\System\SalesChannel\SalesChannelContext;

class DiscountCollector implements CartProcessorInterface
{
    /**
     * @var PercentagePriceCalculator
     */
    private $calculator;

    public function __construct(PercentagePriceCalculator $calculator)
    {
        $this->calculator = $calculator;
    }

    public function process(CartDataCollection $data, Cart $original, Cart $toCalculate, SalesChannelContext $context, CartBehavior $behavior): void
    {
        ....
        /* I WANT TO ACCESS THE DATABASE HERE */
        ....
    }
}
2 Answers

In Shopware 6, you can directly access the Doctrine\DBAL\Connection database object like this:

$connection = \Shopware\Core\Kernel::getConnection();
$connection->executeQuery("DROP TABLE my_table");

Often, it'll be more appropriate to use dependency injection via services.xml.

If you want to use the database directly in Shopware 6, you can do it for example by injecting the Connection like this:

<?php
namespace SomePlugin;
 
use Doctrine\DBAL\Connection;
 
class SomeClass
{
    private $connection;
 
    //inject the connection into your class constructor
    public function __construct (
        Connection $connection
    )
    {
        $this->connection = $connection;
    }
 
    public function someFunction ()
    {
        //define the SQL query
        $sqlQuery = "SELECT * FROM `some_table`";
 
        //execute the query and fetch the results as an array
        $sqlResult = $this->connection->executeQuery($sqlQuery)->fetchAll();
 
        //..or execute the query and get the results row by row
        $sqlResult = $this->connection->executeQuery($sqlQuery);
        foreach ($sqlResult as $sqlResultRow) {
            //do something with this row
        }
 
    }
 
}

I actually wrote an article on my blog about this some time a go: Is using the database directly such a sin?. You can find some more examples and tips there. Generally I do not think that it is a good idea to use the DB directly, but there are some cases, where it makes sense.

If you really need to avoid dependency injection, then perhaps a classical PDO is the way to go:

$options = [
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ];

        //get the connection data from config (XML_PATH_ETC constants define the paths to MySQL login data in your plugin's config)
        $host = $this->config->get(self::XML_PATH_HOST);
        $dbname = $this->config->get(self::XML_PATH_DBNAME);
        $port = $this->config->get(self::XML_PATH_PORT);
        $username = $this->config->get(self::XML_PATH_USERNAME);
        $password = $this->config->get(self::XML_PATH_PASSWORD);

        //create connection string
        $dsn = "mysql:host=$host;dbname=$dbname;port=$port;charset=utf8mb4";

        //connect to the remote database
        try {
            $this->db = new PDO($dsn, $username, $password, $options);
            echo('Connected to the remote DB server ' . $host . ' successfully.');
        } catch (\PDOException $e) {
            echo('Could not connect to the remote DB server ' . $host . '. ERROR: ' . $e->getMessage(), 'error');
        }

This will work, but please just note, that you need to know the login data to the database and have them stored in your plugin's config. Not ideal, if you want to make the plugin universal for installation on various stores, bacause the login data for MySQL would have to be specified in the plugin's config for each such store separately.

Related