I'm writing a package where I need to have PHP objects represent MySQL data types.
For example, I have classes for IntType
, VarCharType
, BlobType
, etc.
Each of these have different constructor arguments. I.e. IntType
has a signed property, VarChar
has collation, etc.
The source for which of these types I need to construct from from the database. So I may have "INT(10) UNSIGNED", "VARCHAR(255)", and "BLOB".
So currently I have a method that examine and pull apart these strings, and it uses a lot of IF and SWITCH statements to determine which class needs to be constructed.
I know this is ugly but I'm not sure on the best way around it. All textbook examples of the Factory pattern are based on producing classes that have identical constructor arguments.
Currently I have two ideas to solve this:
Because I have not see much theory on this, I'm a little unsure of the most ideal direction.
The following is the offending code to show exactly how ugly it is...
<?php
class Factory
{
// ...
/**
* @param string $dataTypeString Data type string as reported by MySQL when executing SHOW FULL COLUMNS FROM table;
* @param null $collation
*/
public function createDataType($dataTypeString, $collation = null)
{
$dataType = null;
if (preg_match('/^(tiny|small|medium|big)?int(eger)?\((\d+)\)( unsigned)?/i', $dataTypeString, $matches)) {
$displayWidth = $matches[3];
$signed = empty($matches[4]);
switch (strtolower($matches[1])) {
case 'tiny':
$dataType = new TinyIntType($displayWidth, $signed);
break;
case 'small':
$dataType = new SmallIntType($displayWidth, $signed);
break;
case 'medium':
$dataType = new MediumIntType($displayWidth, $signed);
break;
case 'big':
$dataType = new BigIntType($displayWidth, $signed);
break;
default:
$dataType = new IntType($displayWidth, $signed);
break;
}
} elseif (preg_match('/^(decimal|float|double)\((\d+),(\d+)\)( unsigned)?/i', $dataTypeString, $matches)) {
$precision = $matches[2];
$scale = $matches[3];
$signed = empty($matches[4]);
switch (strtolower($matches[1])) {
case 'decimal':
$dataType = new DecimalType($precision, $scale, $signed);
break;
case 'double':
$dataType = new DoubleType($precision, $scale, $signed);
break;
case 'float':
$dataType = new FloatType($precision, $scale, $signed);
}
} elseif (preg_match('/^(var)?binary\((\d+)\)$/i', $dataTypeString, $matches)) {
$isVarBinary = !empty($matches[1]);
$length = $matches[2];
if (strtolower($isVarBinary)) {
$dataType = new VarBinaryType($length);
} else {
$dataType = new BinaryType($length);
}
} elseif (preg_match('/^bit\((\d+)\)$/i', $dataTypeString, $matches)) {
$dataType = new BitType($matches[1]);
} else {
$characterSet = !empty($collation) ? $this->getCharacterSet($collation) : null;
if (preg_match('/^(var)?char\((\d+)\)$/i', $dataTypeString, $matches)) {
$isVarChar = !empty($matches[1]);
$length = $matches[2];
if ($isVarChar) {
$dataType = new VarCharType($length, $characterSet, $collation);
} else {
$dataType = new CharType($length, $characterSet, $collation);
}
} elseif (preg_match("/(set|enum)(\('.+'\))/i", $dataTypeString, $matches)) {
$options = $this->parseOptions($matches[2]);
switch (strtolower($matches[1])) {
case 'set':
$dataType = new SetType($options, $characterSet, $collation);
break;
case 'enum':
$dataType = new EnumType($options, $characterSet, $collation);
break;
}
} else {
switch (strtolower($dataTypeString)) {
case 'text':
$dataType = new TextType($characterSet, $collation);
break;
case 'tinytext':
$dataType = new TinyTextType($characterSet, $collation);
break;
case 'mediumtext':
$dataType = new MediumTextType($characterSet, $collation);
break;
case 'longtext':
$dataType = new LongTextType($characterSet, $collation);
break;
case 'blob':
$dataType = new BlobType();
break;
case 'tinyblob':
$dataType = new TinyBlobType();
break;
case 'mediumblob':
$dataType = new MediumBlobType();
break;
case 'longblob':
$dataType = new LongBlobType();
break;
}
}
}
return $dataType;
}
}
Update: This has been reposted at https://codereview.stackexchange.com/questions/134709/a-pattern-to-clean-up-this-factory-method