Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save Calculation in Code or Database?

Tags:

php

mysql

I'm setting up a system which will carry out various calculations on primitive data, and the provide an output based on the calculation.

My main question, is more advice rather than a how-to.

Using a MySQL database, and php to access.

There are currently ~200 different calculations that need done but these fall into 4 calculation types ie one calculation type is x divided by y, while the different calculations will be different primitive values for x or y.

The system will need to carry out the different calculations at various sections of the system, so I've created one function to carry out the calculation. Currently I have the code for the actual calculation (with a tiny bit of error handling) saved in the database, like this;

**calc_id** | **calc_code**
aDIVb       | if($primBVal != 0){ $result = ($primAVal / $primBVal);} else {$result = 'NA';}
aPERCb      | if($primBVal != 0){ $result = (100 / $primBVal) * $primAVal;} else {$result = 'NA';}

etc. These are then calculated in php using eval( $calcArray['calc_code']);

The 4 calculations will never change, though more may be added in the future.

The question is, should I build this code into the PHP function, or is it ok to stay in the database as is? Based on the fact that more may be added, I'm leaning towards the database, as I can add new rows in to handle the new calculation types, rather than having to edit any code.

Hopefully this all makes sense, apologies for the long question and probably dodgy formatting.

Thanks in advance.

like image 846
steven mcdowell Avatar asked Jul 13 '26 14:07

steven mcdowell


1 Answers

Eval is Evil

First of all: don't use eval() unless there is a good reason. And there is never a good reason.

in the worst case eval() makes your application vulnerable to injection attacks and also it's very slow. A bit of research reveals plenty of reasons why eval is a big no-no.

Don't save your calculation code into the database

If you do so and you would like to switch from PHP to another language you would still have PHP code in your database. It makes it really hard to migrate languages. You should always strive to make as many parts of your application as independent as possible.

In this case you would tight-couple the language you use, to the database. That's a bad practice.

Also the only possibilities to run your calculations from the database would be to eval them (which is bad, see above) or to disassemble the string with string operations or regex which causes unnecessary effort.

It's all about Strategy

In order to solve your problem you must execute code dependent of which calculation you need. That could be either done with switch-case-statements or if-statements. But that's also not a very elegant solution. Imagine you would need to execute other operations before calculating in the future, or extend functionality. You would need to update all your cases or if-statements.

There is a nice design-pattern which is called Strategy Pattern. The strategy pattern solves problems when one use-case can be handled differently which is probably what you want.

You want to calculate something (use-case) and there are different calculation types for it (different strategies)

How it works

To implement the Strategy pattern you basically need three things.

  • A class where you inject your strategies. It's basically a wrapper for your strategy tasks.
  • An interface which will be implemented by your strategies
  • Your strategies

Your interface could look like this:

<?php
interface CalculatableInterface {
    
    public function calculate();

}

The interface will make sure that all your strategies provide a method to actually run the calculation. Nothing special.

Next you may want to have a base class that takes your calculation operators as constructor arguments and stores them into properties.

<?php
abstract class Calculatable {

    protected $valueA;
    protected $valueB;

    public function __construct($valueA, $valueB)
    {
        $this->valueA = $valueA;
        $this->valueB = $valueB;
    }

}

Now it's getting serious. We are implementing our strategies.

<?php
class Division extends Calculatable implements CalculatableInterface {

    public function calculate()
    {
        return ($this->valueB != 0) ? $this->valueA / $this->valueB : 'NA';
    }

}

class Percentage extends Calculatable implements CalculatableInterface {

    public function calculate()
    {
        return ($this->valueB != 0) ? (100 / $this->valueB) * $this->valueA : 'NA';
    }

}

Of course you could clean this one up a bit, but what I want to point out here is the class declaration.

We are extending our Calculatable class so that we can pass the arithmetic operations via constructor and we are implementing the CalculatableInterface which tells our class: "Hey! You must provide a calculate method, I don't care whether you want or not.

We'll see later why this is an integral part of the pattern.

So we have two concrete classes that contain the actual code for the actual arithmetic operation. If you would ever need to, you could change it easily as you see. To add more operations just add another class.

Now we will create a class where our strategies can be injected. Later you will instantiate an object of this class and work with it.

Here is how it looks like:

<?php 
class Calculator {

    protected $calculatable;

    public function __construct( CalculatableInterface $calculatable )
    {
        $this->calculatable = $calculatable;
    }

    public function calculate()
    {
        return $this->calculatable->calculate();
    }

}

The most important part here is the constructor. See how we type-hint our interface here. By doing that we make sure that only an object can be injected (Dependency Injection) whose class implements the interface. We do not need to demand a concrete class here. That's the crucial point here.

Also there's a calculate method in there. It's just a wrapper for our strategy to execute it's calculate method.

Wrapping it up

So now we just need to create an object of our Calculator class and pass an object of one of our strategy classes (that contain the code for the arithmetic operations).

<?php
//The corresponding string is stored in your DB
$calculatable = 'Division';

$calc = new Calculator( new $calculatable(15, 100) );
echo $calc->calculate();

Try replacing the string stored in $calculatable to Percentage and you see that the operation for calculating the percentage will be executed.

Conclusion

The strategy pattern allowed you to create a clean interface for working with dynamic tasks that are only made concrete during runtime. Neither your database needs to know how we calculate things, nor your actual calculator does. The only thing we need to make sure is to code against an interface that provides a method to let us calculate things.

like image 157
thpl Avatar answered Jul 16 '26 04:07

thpl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!