Spamworldpro Mini Shell
Spamworldpro


Server : Apache
System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64
User : corals ( 1002)
PHP Version : 7.4.33
Disable Function : exec,passthru,shell_exec,system
Directory :  /home/corals/old/app/code/Cnc/Sales/Console/Command/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/corals/old/app/code/Cnc/Sales/Console/Command/ImportOrders.php
<?php
/**
 * Copyright (c) 2020 Kaliop Digital Commerce (https://digitalcommerce.kaliop.com) All Rights Reserved.
 * https://opensource.org/licenses/OSL-3.0  Open Software License (OSL 3.0)
 * Cnc
 * Radosław Stępień <[email protected]> <[email protected]>
 */
namespace Cnc\Sales\Console\Command;

use Cnc\Sales\Model\Config as SalesConfig;
use Cnc\Store\Model\Config;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DataObject;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Sales\Api\OrderPaymentRepositoryInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Status\HistoryFactory;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Customer\Model\CustomerFactory;
use Magento\Sales\Model\OrderFactory;
use Magento\Sales\Model\Order\ItemFactory;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ImportOrders extends Command
{
    const TABLE_NAME_ORDER = 'cnc_tmp_orders_migration';
    const TABLE_NAME_ORDER_ITEM = 'cnc_tmp_orders_migration_item';
    const TABLE_NAME_ORDER_TOTALS = 'cnc_tmp_orders_migration_totals';
    const TABLE_NAME_ORDER_STATUS = 'cnc_tmp_orders_migration_status';
    private const BATCH_SIZE = 5000;
    private const VALUE_NOT_MIGRATED = 0;
    private const VALUE_MIGRATED = 1;

    private const SHIPPING_METHOD_MAPPING = [
        'homecl_colissimo' => 'colissimo_homecl',
        'pickup_colissimo' => 'colissimo_pickup',
        'mondialrelaypickup_' => 'mondialrelay_pickup',
        'storepickup_storepickup' => 'storepickup_storepickup',
        'custom' => 'custom'
    ];

    private const ORDER_STATUS_MAPPING = [
        1 => SalesConfig::ORDER_STATUS_PENDING_PAYMENT_CODE,
        2 => SalesConfig::ORDER_STATUS_PAIEMENT_RECU_CODE,
        3 => SalesConfig::ORDER_STATUS_EXPEDIE_PAYE_CODE,
        4 => SalesConfig::ORDER_STATUS_CANCELED_CODE,
        5 => SalesConfig::ORDER_STATUS_COMPLETE_CODE,
        6 => SalesConfig::ORDER_STATUS_EXPEDIE_PAIEMENT_30J_CODE,
        7 => SalesConfig::ORDER_STATUS_EXPEDIE_PAIEMENT_RECEP_FACTURE_CODE,
        8 => SalesConfig::ORDER_STATUS_PROCESSING_CODE,
        9 => SalesConfig::ORDER_STATUS_COMPLETE_CODE,
        10 => SalesConfig::ORDER_STATUS_COMPLETE_CODE
    ];

    private const ORDER_STATE_MAPPING = [
        1 => Order::STATE_PENDING_PAYMENT,
        2 => Order::STATE_PROCESSING,
        3 => Order::STATE_PROCESSING,
        4 => Order::STATE_CANCELED,
        5 => Order::STATE_COMPLETE,
        6 => Order::STATE_PROCESSING,
        7 => Order::STATE_PROCESSING,
        8 => Order::STATE_PROCESSING,
        9 => Order::STATE_COMPLETE,
        10 => Order::STATE_COMPLETE
    ];

    /**
     * @var string[]
     */
    private $unsetDataKeys = [
        'orders_date_finished',
        'currency',
        'currency_value',
    ];

    /**
     * @var string[]
     */
    private $unsetItemDataKeys = [
        'products_model',
        'products_name',
        'products_price',
        'final_price',
        'products_tax',
        'products_quantity',
        'products_sockage'
    ];

    /**
     * @var array
     */
    private $remapItemDataKeys = [
        'orders_products_id' => 'item_id',
        'orders_id' => 'order_id',
        'products_model' => 'sku',
        'products_name' => 'name',
        'products_price' => 'price',
        'final_price' => 'row_total',
        'products_tax' => 'tax_amount',
        'products_quantity' => 'qty_ordered',
        'products_sockage' => 'additional_data',
    ];

    /**
     * @var array
     */
    private $totalsDataMapping = [
        'ot_total' => 'grand_total',
        'ot_tax' => 'tax_amount',
        'ot_subtotal' => 'subtotal',
        'ot_shipping' => 'shipping_amount',
        'ot_coupon' => 'discount_amount'
    ];

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;

    /**
     * @var OrderFactory
     */
    protected $orderFactory;

    /**
     * @var ItemFactory
     */
    protected $orderItemFactory;

    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var CustomerFactory
     */
    protected $customerFactory;

    /**
     * @var OrderPaymentRepositoryInterface
     */
    protected $orderPaymentRepository;

    /**
     * @var HistoryFactory
     */
    protected $statusHistoryFactory;

    /**
     * @var array
     */
    protected $storeData = [];

    /**
     * @var array
     */
    private $orderData = [];

    /**
     * @var array
     */
    private $itemData = [];

    /**
     * @var array
     */
    private $totalsData = [];

    /**
     * @var array
     */
    private $statusesData = [];

    /**
     * @var array
     */
    private $itemsOrderData = [];

    /**
     * @var array
     */
    private $totalsOrderData = [];

    /**
     * @var array
     */
    private $statusesOrderData = [];

    /**
     * @var array
     */
    private $migratedOrderEntityIds = [];

    /**
     * @var array
     */
    private $migratedOrderItemEntityIds = [];

    /**
     * @var array
     */
    private $migratedOrderTotalsEntityIds = [];

    /**
     * @var array
     */
    private $migratedOrderStatusEntityIds = [];

    /**
     * @var array
     */
    protected $addressOrderData = [];

    /**
     * @var array
     */
    protected $paymentOrderData = [];

    private $output;

    /**
     * ImportOrders constructor.
     * @param ResourceConnection $resourceConnection
     * @param OrderFactory $orderFactory
     * @param ItemFactory $orderItemFactory
     * @param StoreManagerInterface $storeManager
     * @param CustomerFactory $customerFactory
     * @param OrderPaymentRepositoryInterface $orderPaymentRepository
     * @param HistoryFactory $statusHistoryFactory
     * @param string|null $name
     */
    public function __construct(
        ResourceConnection $resourceConnection,
        OrderFactory $orderFactory,
        ItemFactory $orderItemFactory,
        StoreManagerInterface $storeManager,
        CustomerFactory $customerFactory,
        OrderPaymentRepositoryInterface $orderPaymentRepository,
        HistoryFactory $statusHistoryFactory,
        string $name = null
    ) {
        parent::__construct($name);
        $this->resourceConnection = $resourceConnection;
        $this->orderFactory = $orderFactory;
        $this->orderItemFactory = $orderItemFactory;
        $this->storeManager = $storeManager;
        $this->customerFactory = $customerFactory;
        $this->orderPaymentRepository = $orderPaymentRepository;
        $this->statusHistoryFactory = $statusHistoryFactory;
    }

    /**
     * Configure command name and desc
     */
    public function configure()
    {
        $this->setName('sales:orders_import:create_orders');
        $this->setDescription('Import orders data');
        parent::configure();
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int|void
     * @throws LocalizedException
     */
    public function execute(InputInterface $input, OutputInterface $output)
    {
        $this->output = $output;
        $this->initCncStoreData();
        while ($this->loadData()) {
            $this->prepareData();
            $this->toMagento();
            $this->updateIsMigratedFlag();
        }
    }

    /**
     * Load data from tmp tables
     */
    protected function loadData()
    {
        $ordersSelect = $this->getConnection()->select()
            ->from($this->getConnection()->getTableName(self::TABLE_NAME_ORDER))
            ->where('is_migrated = ?', self::VALUE_NOT_MIGRATED)
            ->limit(self::BATCH_SIZE);

        $this->orderData = $this->getConnection()->fetchAll($ordersSelect);
        $orderIds = array_column($this->orderData, 'orders_id');

        if (empty($orderIds)) {
            $this->output->writeln('Records to migrate not found.');
            return;
        }

        $this->itemData = $this->fetchMigrationRecords(
            self::TABLE_NAME_ORDER_ITEM,
            'orders_id',
            $orderIds
        );

        $this->totalsData = $this->fetchMigrationRecords(
            self::TABLE_NAME_ORDER_TOTALS,
            'orders_id',
            $orderIds
        );

        $this->statusesData = $this->fetchMigrationRecords(
            self::TABLE_NAME_ORDER_STATUS,
            'orders_id',
            $orderIds
        );

        $this->migratedOrderEntityIds = $orderIds;
        $this->migratedOrderItemEntityIds = array_column($this->itemData, 'orders_products_id');
        $this->migratedOrderTotalsEntityIds = array_column($this->totalsData, 'orders_total_id');
        $this->migratedOrderStatusEntityIds = array_column($this->statusesData, 'orders_status_history_id');

        return count($this->orderData);
    }

    /**
     * Prepare data structure before import to magento
     */
    protected function prepareData()
    {
        $this->output->writeln('Migration New Orders: Started PREPARING DATA...');
        //// Get Order Data
        foreach ($this->orderData as $key => &$dataRow) {
            $dataRow['created_at'] = strtotime($dataRow['date_purchased']);
            $dataRow['updated_at'] = strtotime($dataRow['last_modified']);
            $dataRow['email_sent'] = 0;

            //// Get Order Address Data
            $this->addressOrderData[$dataRow['orders_id']]['shipping'] =
                [
                    'firstname' => explode(' ', $dataRow['delivery_name'], 2)[0] ?? '',
                    'lastname' => explode(' ', $dataRow['delivery_name'], 2)[1] ?? '',
                    'company' => $dataRow['delivery_company'] ?? '',
                    'street' => $dataRow['delivery_street_address'] ?? '',
                    'city' => $dataRow['delivery_city'] ?? '',
                    'country_id' => $dataRow['delivery_country'] ?? '',
                    'region' => $dataRow['delivery_state'] ?? '',
                    'postcode' => $dataRow['delivery_postcode'] ?? '',
                    'telephone' => $dataRow['customers_telephone'] ?? '',
                    'telephone_mobile' => $dataRow['customers_telephone'] ?? ''
                ];
            $this->addressOrderData[$dataRow['orders_id']]['billing'] =
                [
                    'firstname' => explode(' ', $dataRow['billing_name'], 2)[0] ?? '',
                    'lastname' => explode(' ', $dataRow['billing_name'], 2)[1] ?? '',
                    'company' => $dataRow['billing_company'] ?? '',
                    'vat_id' => $dataRow['billing_tva_intracom'] ?? '',
                    'street' => $dataRow['billing_street_address'] ?? '',
                    'city' => $dataRow['billing_city'] ?? '',
                    'country_id' => $dataRow['billing_country'] ?? '',
                    'region' => $dataRow['billing_state'] ?? '',
                    'postcode' => $dataRow['billing_postcode'] ?? '',
                    'telephone' => $dataRow['customers_telephone'] ?? '',
                    'telephone_mobile' => $dataRow['customers_telephone'] ?? ''
                ];

            //// Get Order Payment Data
            $this->paymentOrderData[$dataRow['orders_id']] =
                [
                    'method' => $dataRow['payment_method'] ?? '',
                    'cc_exp_month' => $dataRow['cc_expires'] ?? '',
                    'cc_number_enc' => $dataRow['cc_number'] ?? '',
                    'cc_owner' => $dataRow['cc_owner'] ?? '',
                    'cc_type' => $dataRow['cc_type'] ?? '',
                ];

            foreach ($dataRow as $dataRowKey => $dataRowValue) {
                if (trim($dataRowValue) == '') {
                    unset($dataRow[$dataRowKey]);
                }
            }

            foreach ($this->unsetDataKeys as $unsetDataKey) {
                if (isset($dataRow[$unsetDataKey])) {
                    unset($dataRow[$unsetDataKey]);
                }
            }
        }

        //// Get Order Items Data
        foreach ($this->itemData as &$dataRow) {
            foreach ($this->remapItemDataKeys as $remapItemDataKey => $remapToItemDataKey) {
                if (isset($dataRow[$remapItemDataKey])) {
                    $dataRow[$remapToItemDataKey] = $dataRow[$remapItemDataKey];
                }
            }
            foreach ($this->unsetItemDataKeys as $unsetItemDataKey) {
                if (isset($dataRow[$unsetItemDataKey])) {
                    unset($dataRow[$unsetItemDataKey]);
                }
            }
            $this->itemsOrderData[$dataRow['orders_id']][$dataRow['orders_products_id']] = $dataRow;
        }

        //// Get Order Totals Data
        foreach ($this->totalsData as $dataRow) {
            if ($dataRow['class']) {
                if ($dataRow['class'] == 'ot_coupon') {
                    $dataRow['value'] = '-' . $dataRow['value'];
                }
                if (isset($this->totalsDataMapping[$dataRow['class']])) {
                    $this->totalsOrderData[$dataRow['orders_id']][$this->totalsDataMapping[$dataRow['class']]]
                        = $dataRow['value'];
                }
            }
        }

        //Group statuses updates by orders IDs
        foreach ($this->statusesData as $dataRow) {
            if (isset($dataRow['orders_id'])) {
                $this->statusesOrderData[$dataRow['orders_id']][] = $dataRow;
            }
        }

        $this->output->writeln('Migration New Orders: Ended PREPARING DATA');
    }

    /**
     * Import data to magento
     */
    protected function toMagento()
    {
        $this->output->writeln('Migration New Orders: Started IMPORTING DATA...');

        if (count($this->orderData) > 0) {
            $this->output->writeln(count($this->orderData) . ' orders to be imported');

            $newOrdersImported = 0;
            foreach ($this->orderData as &$dataRow) {
                try {
                    $isOrderCreated = $this->createMageOrder($dataRow);

                    if ($isOrderCreated) {
                        $newOrdersImported++;
                    }
                } catch (\Exception $e) {
                    $this->output->writeln($e->getMessage());
                }
            }
            $this->output->writeln("Imported {$newOrdersImported} new orders");

        } else {
            $this->output->writeln('No Data founded for executing ORDERS IMPORT');
        }

        $this->output->writeln('Migration New Orders: Ended IMPORTING DATA');
    }

    /**
     * @param $dataRow
     * @return bool
     */
    protected function createMageOrder($dataRow)
    {
        $storeId = $this->storeData['store_id'];
        $websiteId = $this->storeData['website_id'];

        $orderId = $dataRow['orders_id'];
        $customerId = $dataRow['customers_id'];

        unset($dataRow['orders_id']);
        unset($dataRow['customers_id']);

        // load customer by email address
        if ($this->isEmailValidated($dataRow['customers_email_address'])) {
            $email = strtolower($dataRow['customers_email_address']);

            $customer = $this->getCustomerData($customerId, $email);
            if (!$customer || !$customer->getEntityId()) {
                $this->output->writeln("Customer with email {$email} / id {$customerId} does Not exist");
                return false;
            }

            // Load order
            $order = $this->orderFactory->create()->setStoreId($storeId);

            // Set customer data
            $order->setCustomerId($customer->getEntityId())
                ->setStoreId($storeId)
                ->setCustomerEmail($customer->getEmail())
                ->setCustomerFirstname($customer->getFirstname())
                ->setCustomerLastname($customer->getLastname())
                ->setCustomerGroupId($customer->getGroupId())
                ->setCustomerIsGuest(0);

            // Set currency data
            $order->setGlobalCurrencyCode('EUR')
                ->setBaseCurrencyCode('EUR')
                ->setStoreCurrencyCode('EUR')
                ->setOrderCurrencyCode('EUR');

            // Set payment data
            $orderPayment = $this->orderPaymentRepository->create();
            foreach ($this->paymentOrderData[$orderId] as $key => $data) {
                $orderPayment->setData($key, $data);
            }
            $order->setPayment($orderPayment);

            // Set address data
            if (array_key_exists($orderId, $this->addressOrderData)) {
                $customerAddress = $this->addressOrderData[$orderId];

                if (array_key_exists('billing', $customerAddress)) {
                    $billingAddressCustomer = $this->addressOrderData[$orderId]['billing'];
                } else {
                    $this->output->writeln(
                        "Missing order Billing address data for email - {$email} and orderId - {$orderId}",
                        'warning'
                    );
                    return false;
                }

                if (array_key_exists('shipping', $customerAddress)) {
                    $shippingAddressCustomer = $this->addressOrderData[$orderId]['shipping'];
                } else {
                    $this->output->writeln(
                        "Missing order Shipping address data for email - {$email} and orderId - {$orderId}",
                        'warning'
                    );
                    return false;
                }

                //Init object manager here, because of errors with addition object manager instance in
                // this console command's constructor method
                $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
                $configLoader = $objectManager->get(\Magento\Framework\ObjectManager\ConfigLoaderInterface::class);
                $objectManager->configure($configLoader->load('frontend'));

                $billingAddress = $objectManager->create(
                    \Magento\Sales\Model\Order\Address::class,
                    ['data' => $billingAddressCustomer]
                );

                $shippingAddress = $objectManager->create(
                    \Magento\Sales\Model\Order\Address::class,
                    ['data' => $shippingAddressCustomer]
                );

                $order->setBillingAddress($billingAddress);
                $order->setShippingAddress($shippingAddress);
            } else {
                $this->output->writeln("Missing order address data for email - {$email} and orderId - {$orderId}");
                return false;
            }

            // Set items
            if (array_key_exists($orderId, $this->itemsOrderData)) {
                foreach ($this->itemsOrderData[$orderId] as $item) {
                    $orderItem = $this->orderItemFactory->create();

                    $item['store_id'] = $storeId;
                    $item['product_type'] = 'simple';
                    unset($item['order_id']);
                    unset($item['item_id']);
                    unset($item['parent_item_id']);
                    unset($item['quote_item_id']);

                    foreach ($item as $key => $data) {
                        $orderItem->setData($key, $data);
                    }

                    $order->addItem($orderItem);
                }
            } else {
                $this->output->writeln("Missing order items data for email - {$email} and orderId - {$orderId}");
                return false;
            }

            // Set totals
            if (array_key_exists($orderId, $this->totalsOrderData)) {
                foreach ($this->totalsOrderData[$orderId] as $key => $value) {
                    $order->setData($key, $value);
                }
            } else {
                $this->output->writeln("Missing totals data for email - {$email} and orderId - {$orderId}");
                return false;
            }

            //Unset useless data
            unset($dataRow['entity_id']);
            unset($dataRow['customer_true_email']);
            unset($dataRow['quote_id']);
            unset($dataRow['quote_address_id']);
            $dataRow['origin_order_id']= $orderId; //Set old orders_id from oscommerce
            $dataRow['store_id']= $storeId;
            $dataRow['is_exported']= 1;

            if ($dataRow['order_canceled']) {
                $order->setState('canceled');
                $order->setStatus('canceled');
            } else {
                $order->setState(self::ORDER_STATE_MAPPING[$dataRow['orders_status']]);
                $order->setStatus(self::ORDER_STATUS_MAPPING[$dataRow['orders_status']]);
            }

            unset($dataRow['orders_status']);

            // Set all data
            foreach ($dataRow as $key => $data) {
                $order->setData($key, $data);
            }

            if (isset($this->statusesOrderData[$orderId])) {
                foreach ($this->statusesOrderData[$orderId] as $statusHistoryItem) {
                    $statusHistory = $this->statusHistoryFactory->create();
                    $statusHistory->setIsCustomerNotified(0);
                    $statusHistory->setIsVisibleOnFront(0);
                    $statusHistory->setComment($statusHistoryItem['object'] . ' | ' . $statusHistoryItem['comments']);
                    if (isset(self::ORDER_STATUS_MAPPING[$statusHistoryItem['orders_status_id']])) {
                        $statusHistory->setStatus(self::ORDER_STATUS_MAPPING[$statusHistoryItem['orders_status_id']]);
                    }
                    $statusHistory->setCreatedAt($statusHistoryItem['date_added']);
                    $statusHistory->setEntityName(Order::ENTITY);
                    $order->addStatusHistory($statusHistory);
                }
            }

            try {
                $order->save();
                return true;
            } catch (\Exception $e) {
                $this->output->writeln("Can't create order for orderId={$orderId} : {$e->getMessage()}");
                return false;
            }
        } else {
            $this->output->writeln("Invalid email {$dataRow['customers_email_address']}");
            return false;
        }
    }

    /**
     * Update correctly migrated orders flag
     */
    protected function updateIsMigratedFlag(): void
    {
        $this->updateIsMigratedColumn(
            self::TABLE_NAME_ORDER,
            'orders_id',
            $this->migratedOrderEntityIds
        );
        $this->updateIsMigratedColumn(
            self::TABLE_NAME_ORDER_ITEM,
            'orders_products_id',
            $this->migratedOrderItemEntityIds
        );
        $this->updateIsMigratedColumn(
            self::TABLE_NAME_ORDER_TOTALS,
            'orders_total_id',
            $this->migratedOrderTotalsEntityIds
        );
    }

    /**
     * @param string $tableName
     * @param string $columnName
     * @param array $entityIds
     */
    protected function updateIsMigratedColumn(string $tableName, string $columnName, array $entityIds): void
    {
        $this->getConnection()->update(
            $this->getConnection()->getTableName($tableName),
            ['is_migrated' => self::VALUE_MIGRATED],
            ["{$columnName} IN (?)" => array_values($entityIds)]
        );
    }

    /**
     * @throws LocalizedException
     */
    protected function initCncStoreData()
    {
        $websiteFr = $this->storeManager->getWebsite(Config::DEFAULT_WEBSITE);
        $websiteId = $websiteFr->getId();

        $storeCode = Config::STORE_CODE_FR_EU;
        // get array of stores with store code as key
        $stores = $this->storeManager->getStores(true, true);
        // check stores array for this store code
        if (isset($stores[$storeCode])) {
            $storeId = $stores[$storeCode]->getId();
        } else {
            throw new LocalizedException(__("Store Id Not founded for Store Code - $storeCode"));
        }

        $this->storeData = [
            'website_code' => Config::DEFAULT_WEBSITE,
            'website_id' => $websiteId,
            'store_id' => $storeId
        ];
    }

    /**
     * @param $email
     * @return bool
     */
    protected function isEmailValidated($email)
    {
        if (!empty($email)) {
            try {
                return \Zend_Validate::is($email, 'EmailAddress');
            } catch (\Exception $e) {
                $this->output->writeln($e->getMessage());
                return false;
            }
        }

        return false;
    }

    /**
     * @param string $tableName
     * @param string $orderIdColumnName
     * @param array $orderIds
     * @return array
     */
    protected function fetchMigrationRecords(string $tableName, string $orderIdColumnName, array $orderIds): array
    {
        $select = $this->getConnection()->select()
            ->from($this->getConnection()->getTableName($tableName))
            ->where('is_migrated = ?', self::VALUE_NOT_MIGRATED)
            ->where("{$orderIdColumnName} IN (?)", array_values($orderIds));

        return $this->getConnection()->fetchAll($select);
    }

    /**
     * @return AdapterInterface
     */
    private function getConnection(): AdapterInterface
    {
        return $this->resourceConnection->getConnection();
    }

    /**
     * @param $customerId
     * @param $email
     * @return DataObject|null
     */
    public function getCustomerData($customerId, $email)
    {
        $sql = 'SELECT entity_id, email, customer_id, firstname, lastname, group_id FROM customer_entity WHERE customer_id = ' . $customerId;
        $data = $this->resourceConnection->getConnection()->fetchRow($sql);
        if (is_array($data) && $data['entity_id']) {
            return new DataObject($data);
        }

        $sql = 'SELECT entity_id, email, customer_id, firstname, lastname FROM customer_entity WHERE email = \'' . $email . '\'';
        $data = $this->resourceConnection->getConnection()->fetchRow($sql);
        return (is_array($data) && $data['entity_id']) ? new DataObject($data) : null;
    }
}

Spamworldpro Mini