Home > database >  Replacing conditionals with polymorphism in php
Replacing conditionals with polymorphism in php

Time:06-08

I have a form with a <select> tag. The select has 3 options with values: Book, DVD, and Table. I want to load classes BookType, DVDType and TableTable respectively which each option is selected. The only function of these classes is to change display style of different divs from none to block. I know i can easily accomplish this using conditionals but i was specifically required not to, so i want to use abstract classes instead. I have created an abstract ProductType :

abstract class ProductType {
    
    abstract public function displayInputs();
    
}

I also created the 3 other classes that extends the ProductType like so;

BookType.php

class BookType extends ProductType {
        
    public function displayInputs(){
         
       <script language="javascript">

            document.getElementById("book-div").style.display = "block";
        
        </script>
    

    }
}

DVDType.php

class DVDType extends ProductType {
   
    public function displayInputs(){
        <script language="javascript">

            document.getElementById("dvd-div").style.display = "block";
        
        </script>

    }
}

TableType.php

class TableType extends ProductType {
        
    public function displayInputs(){
       <script language="javascript">

            document.getElementById("table-div").style.display = "block";
        
        </script>

    }
}

Now the issue is i don't know how to get the selected option and call on the appropriate class so that the different divs would show up. Please i would really appreciate any help i can get.

NB: I'm using MVC model.

Thank you in advance.

CodePudding user response:

You shouldn't really do it that way, because if you do submit a form page refreshes either way and it's easier to directly change the element instead of adding a script to the page.

If you really want to do it this way then

  1. use $_POST to get value of chosen option from submitted form
  2. depending on the option pick right class to instantiate
  3. call the method displayInputs on object created in step 2

You will have to fix the displayInputs as it doesn't output the script, you should change values of that function to strings and return them, after that echo what you received.

public function displayInputs(){
   return <<<'EOT'
            <script language="javascript">

            document.getElementById("table-div").style.display = "block";
    
            </script>
EOT;
}

This uses nowdoc syntax, in this case it's the best way to output a string spanning multiple lines. There is also heredoc but that does parsing (which we don't need).

If you look closely you will see that step 2 requires use of conditionals and you can't really escape that. You can hide it in a factory or use a dependency injection container, but still there is a conditional somewhere. You can use match expression but that is a conditional too.

Well, there is a specific way to escape conditionals, you create an array keyed with options and with values of functions creating right class object. I'm not sure this is what they had in mind when you were asked to not use conditionals, but I don't see any other kind of safe and maintainable way.

$options = [
    'book' => fn()=>return new BookType(),
    'dvd' => fn()=>return new DVDType(),
    'table' => fn()=> return new TableType()
];
$instance = $options[$_POST['option']]();
echo $instance->displayInputs();

The fn() notation is called arrow functions, this creates functions for calling later (also named closures or lambdas). Later you pick a function and call it (look at parentheses at the end of $instance line). This gives you correct object and all that is left is to call the displayInputs method. This of course assumes what values your form sends, it may not work without changes.

CodePudding user response:

It's better to leave the "View" logic to the template engine, for example, twig

If this is not possible, use form package, such as symfony/form

If two options above are not possible, write own "form builder" by Composite design pattern

CodePudding user response:

In my opinion, that's a lot of overkill. If you are using PHP, why output all three DIVs when you just want to display one of them? Should they be able to be displayed later with some additional JavaScript code?

Ok, if you really need to do that, why using JavaScript to do CSS stuff when it's only used to display certain sections (or not).

If you have access to the <HEAD> section (and that's not too complicated due to your framework), I would add something like this:

if (in_array($_POST['option'] ?? '', ['book', 'dvd', 'table'])) {
    echo '<link rel="stylesheet" href="/styles/'.$_POST['option'].'.css">';
}

Then have these three CSS-files:

/* book.css */
book-div  { display: block; }
dvd-div   { display: none; }
table-div { display: none; }

/* dvd.css */
book-div  { display: none; }
dvd-div   { display: block; }
table-div { display: none; }

/* table.css */
book-div  { display: none; }
dvd-div   { display: none; }
table-div { display: block; }

In this way you just use the exact CSS file you need to display one of the three DIVs. This doesn't hurt the option to display one of the others later by JS.

  • Related