15

Below is the MapperInterface.php

I'm trying to figure out how to add an if-else statement into the const. mapping array. Something like so:

if (LIN02 == “VN”) 
o   Treat LIN03 as the SKU
·         else if (LIN04 == “VN”) 
o   Treat LIN05 as the SKU

<?php

declare(strict_types=1);

namespace Direct\OrderUpdate\Api;

use Direct\OrderUpdate\Api\OrderUpdateInterface;

/**
 * Interface MapperInterface
 * Translates parsed edi file data to a \Direct\OrderUpdate\Api\OrderUpdateInterface
 * @package Direct\OrderUpdate\Api
 */
interface MapperInterface
{
    /**
     * Mapping array formatted as MAPPING[segemntId][elemntId] => methodNameToProcessTheValueOfElement
     * @var array
     */
    const MAPPING = [
        'DTM' => ['DTM02' => 'processCreatedAt'],   // shipment.created_at
        'PRF' => ['PRF01' => 'processIncrementId'], // order.increment_id
        'LIN' => ['LIN05' => 'processSku'],         // shipment.items.sku
        'SN1' => ['SN102' => 'processQty'],         // shipment.items.qty
        'REF' => ['REF02' => 'processTrack']        // shipment.tracks.track_number, shipment.tracks.carrier_code
    ];

    /**
     * Mapping for carrier codes
     * @var array
     */
    const CARRIER_CODES_MAPPING = ['FED' => 'fedex'];

    /**
     * @return array
     */
    public function getMapping(): array;

    /**
     * @param array $segments
     * @return OrderUpdateInterface
     */
    public function map(array $segments): OrderUpdateInterface;
}

I hope that makes sense. Not sure if there is a better way to go about it but ultimately I need more than 1 "LIN" segmentId. Maybe add a new function and use this condition?

NEW FILE ANSWER ***

    <?php

    declare(strict_types=1);

    namespace Direct\OrderUpdate\Api;

    use Direct\OrderUpdate\Api\OrderUpdateInterface;

    /**
     * Abstract Mapper
     * Translates parsed edi file data to a \Direct\OrderUpdate\Api\OrderUpdateInterface
     * @package Direct\OrderUpdate\Api
     */

    abstract class AbstractMapper{
    // Here we add all the methods from our interface as abstract
    public abstract function getMapping(): array;
    public abstract function map(array $segments): OrderUpdateInterface;

    // The const here will behave the same as in the interface
    const CARRIER_CODES_MAPPING = ['FED' => 'fedex'];

    // We will set our default mapping - notice these are private to disable access from outside
    private const MAPPING = ['LIN' => [
    'LIN02' => 'VN',
    'LIN01' => 'processSku'],
    'PRF' => ['PRF01' => 'processIncrementId'],
    'DTM' => ['DTM02' => 'processCreatedAt'],
    'SN1' => ['SN102' => 'processQty'],
    'REF' => ['REF02' => 'processTrack']];

    private $mapToProcess = [];

    // When we initiate this class we modify our $mapping member according to our new logic
    function __construct() {
    $this->mapToProcess = self::MAPPING; // init as
    if ($this->mapToProcess['LIN']['LIN02'] == 'VN')
    $this->mapToProcess['LIN']['LIN03'] = 'processSku';
    else if ($this->mapToProcess['LIN']['LIN04'] == 'VN')
        $this->mapToProcess['LIN']['LIN05'] = 'processSku';
    }

    // We use this method to get our process and don't directly use the map
    public function getProcess($segemntId, $elemntId) {
    return $this->mapToProcess[$segemntId][$elemntId];
    }

   }

class Obj extends AbstractMapper {
    // notice that as interface it need to implement all the abstract methods
    public function getMapping() : array {
        return [$this->getMapping()];
    }
    public function map() : array {
        return [$this->map()];
    }

}

class Obj extends AbstractMapper {
    // notice that as interface it need to implement all the abstract methods
    public function getMapping() : array {
        return [$this->getMapping()];
    }
    public function map() : array {
        return [$this->map()];
    }

}
Singleton
  • 85
  • 2
  • 5
  • 16

2 Answers2

6

As you can see here - const variable cannot be change or hold logic. Notice that interface cannot hold logic as well - so you cannot do that in your interface.

I think the better solution for your issue is to use a abstract class. I will be the same as your interface (you can see the discussion about the different here but I think it will be the same for your needs).

I would recommend to create abstract class as this:

abstract class AbstractMapper{
    // here add all the method from your interface as abstract
    public abstract function getMapping(): array;
    public abstract function map(array $segments): OrderUpdateInterface;

    // the const here will behave the same as in the interface
    const CARRIER_CODES_MAPPING = ['FED' => 'fedex'];

    // set your default mapping - notice those are private to disable access from outside
    private const MAPPING = ['LIN' => [
                                'LIN02' => 'NV', 
                                'LIN01' => 'processSku'], 
                             'PRF' => [
                                'PRF01' => 'processIncrementId']];
    private $mapToProcess = [];


    // when initiate this class modify your $mapping member according your logic
    function __construct() {
        $this->mapToProcess = self::MAPPING; // init as 
        if ($this->mapToProcess['LIN']['LIN02'] == 'NV')
            $this->mapToProcess['LIN']['LIN03'] = 'processSku';
        else if ($this->mapToProcess['LIN']['LIN04'] == 'NV')
            $this->mapToProcess['LIN']['LIN05'] = 'processSku';
     }

    // use method to get your process and don't use directly the map
    public function getProcess($segemntId, $elemntId) {
        return $this->mapToProcess[$segemntId][$elemntId];
    }

}

Now you can declare object that inherited as:

class Obj extends AbstractMapper {
    // notice that as interface it need to implement all the abstract methods
    public function getMapping() : array {
        return [];
    }
}

Example for use is:

$obj  = New Obj();
print_r($obj->getProcess('LIN', 'LIN01'));

Note that it seems that your logic is not changing so I put new variable and set it during the construct. If you want you can dump it and just modify the return value of the getProcess function - put all the logic there.

Another option is to make the $mapToProcess public and access it directly but I guess better programing is to use the getter method.

Hope that helps!

dWinder
  • 11,597
  • 3
  • 24
  • 39
  • I should be able to integrate/add that whole abstract class in my same file just below the last function public function map(array $segments): OrderUpdateInterface; } **HERE** – Singleton Jan 16 '20 at 16:42
  • So now i can just override all the old code and use this abstract class? I marked the answer as correct and very helpful my friend. @dWinder – Singleton Jan 16 '20 at 19:02
  • Yes you can. There are difference between interface and abstract class but for the majority of the case it act the same (you can read about it in the link at the beginning of the post). – dWinder Jan 16 '20 at 19:13
  • I think in the logic I still need to add this correct? else if ($this->mapToProcess['LIN']['LIN04'] == 'VN') $this->mapToProcess['LIN']['LIN05'] = 'processSku'; – Singleton Jan 21 '20 at 17:11
  • or is that covered already in the logic that you put together? – Singleton Jan 21 '20 at 17:12
  • 1
    You should add that as well. I only put some of it as example of where the logic should be. I will edit with that as well to make the code cover it – dWinder Jan 21 '20 at 19:47
  • Sorry last question.. Does this need to be at the bottom of the file as well? class Obj extends AbstractMapper { // notice that as interface it need to implement all the abstract methods public function getMapping() : array { return []; } } – Singleton Jan 21 '20 at 21:33
  • Doesn't have to be at the same file but can be. It should be after the declaration of the base class. Notice that in my code implement only one function (`getMapping`) but you need to do that for all the abstract methods you define in the abstract class – dWinder Jan 21 '20 at 21:44
  • I revised my Original post and put the new file answer as how it should look. Where we instantiate the object i just put at the bottom of the file. Does everything look perfect now? @dWinder (sorry for all the questions.. last one i promise) =] – Singleton Jan 22 '20 at 16:34
  • You missing implementation of the `map` function who we define as abstract function in the abstract class. You should add that implementation as well in the Obj class (the same as the `getMapping` function) - without that implementation you cannot declare the Obj class (else you want to make him abstract as well but that different flow). Notice to implement those method as you need (the return of the empty array I did is just example) – dWinder Jan 22 '20 at 17:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206465/discussion-between-singleton-and-dwinder). – Singleton Jan 22 '20 at 18:44
5

You can't add an if-else statement inside the constant definition. The closest to what you are looking for is probably this:

const A = 1;
const B = 2;

// Value of C is somewhat "more dynamic" and depends on values of other constants
const C = self::A == 1 ? self::A + self::B : 0;

// MAPPING array inherits "more dynamic" properties of C
const MAPPING = [
    self::A,
    self::B,
    self::C,
];

Will output:

0 => 1
1 => 2
2 => 3

In other words, you will need to break your array apart into separate constants, then do all conditional defines, then construct the final MAPPING array from resulting constant values.

Karolis
  • 2,580
  • 2
  • 16
  • 31