1
\$\begingroup\$

I'm importing data from file to many tables in database. It may look like this:

First Name, Last Name, Meeting
John, Doe, 2020-04-24 08:00:00

Some fields like "First Name" and "Last Name" are being saved in one table and other fields like "Meeting" are being saved in different tables. There are a lot of more fields and a lot of more rows in my case.

I create handler for each table and put it in a handler chain to reduce amount of code. Each handler knows which fields it can handle. My base handler class looks like this:

abstract class TableHandler
{
    protected $nextHandler;

    abstract public function isFieldSupported(string $field): bool;
    abstract public function handle(string $fieldName, string $fieldValue);

    public function next(TableHandler $nextHandler): TableHandler
    {
        $this->nextHandler = $nextHandler;
    }

    public function handleField(string $fieldName, string $fieldValue)
    {
        $this->handle($fieldName, $fieldValue);

        if ($this->nextHandler()) {
            $this->nextHandler->handleField($fieldName, $fieldValue);
        }
    }

    public function reset(): void
    {
        $this->resetHandler();

        if ($this->nextHandler()) {
            $this->nextHandler->reset();
        }
    }

    private function nextHandler(): bool
    {
        return $this->nextHandler !== null;
    }
}

I use my handler chain this way:

$table1->next($table2)
    ->next($table3);

foreach ($data as $row) {
    foreach ($headers as $index => $header) {
        $table1->handleField($header, $row[$index]);
    }

    $id = $table1->save();
    $table2->save($id);
    $table3->save($id);

    $table1->reset();
}

As you can see I was able to handle fields and reset all handlers by using only first handler. Chain does the rest of the job. The problem is that I don't know how can I improve saving data. When I insert record in first table I get its ID. I need to pass this ID to further handlers because rows that I insert in those tables are related to row from first table so ID is needed. Bacause of it I need to save data separately. I would like to avoid it. Is it possible?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I'm not to sure about your base handler. It has methods like handle() and handleField() that look very similar. One will probably do. I also noted that the save() method is completely missing from your code. Why? You use it, so it should be there. Or at least in an usage example.

As to your actual problem with saving, you can hand over the id's as an argument to the next save() in the clain. Something like:

public function save(array $insertIds = [])
{
    // ..... put needed insert ids into data here ....
    // then perform the insert and get the new insert id 
    $newInsertId = $this->insertRow($this->table,$this->data);
    // add that to the array of insert ids
    $insertIds[$this->table] = $newInsertId;
    // progress down the chain, if possible
    if ($this->nextHandler()) {
        $this->nextHandler->save($insertIds);
    }
}

Note that in the $insertIds array the keys are table names, and the values are the actual insert ids. This way all insert ids are available to save() methods down the chain.

It could be useful to return the $insertIds.

Note that the order of chaining is important. You have to think about that when you create the chain. Perhaps it would be a good idea to allow for multiple next handlers.

\$\endgroup\$
1
  • \$\begingroup\$ I created handle() and handleField() methods to not duplicate checking if next handler exists. I didn't put save() method in abstract class because of difference between first handler's save() method and other handlers. I wanted to show only essence of my problem so I didn't put handler's implementation in the above example. Your idea is nice but I'm afraid that in the future some programmer could mistakenly use handlers in wrong order. In my solution I force to call save() on first handler but huge disadvantage is that I can't use this method in the chain. \$\endgroup\$ Commented Apr 24, 2020 at 20:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.