Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
56.67% covered (warning)
56.67%
17 / 30
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
EmptyIntegerBehavior
56.67% covered (warning)
56.67%
17 / 30
66.67% covered (warning)
66.67%
2 / 3
33.31
0.00% covered (danger)
0.00%
0 / 1
 beforeSave
45.83% covered (danger)
45.83%
11 / 24
0.00% covered (danger)
0.00%
0 / 1
34.89
 _getSchema
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 _isIntegerType
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * EmptyIntegerBehavior
5 *
6 * Converts empty strings to appropriate values for integer columns:
7 * - Empty string → 0 for NOT NULL integer columns
8 * - Empty string → null for nullable integer columns
9 *
10 * This prevents database errors when saving form data with empty integer fields.
11 */
12class EmptyIntegerBehavior extends ModelBehavior
13{
14    /**
15     * Cached schema information per model
16     *
17     * @var array
18     */
19    protected $_schemaCache = [];
20
21    /**
22     * Before save callback
23     *
24     * @param Model $model Model instance
25     * @param array $options Options array
26     * @return bool True to continue save, false to abort
27     */
28    public function beforeSave(Model $model, $options = [])
29    {
30        if (empty($model->data[$model->alias]))
31            return true;
32
33        $schema = $this->_getSchema($model);
34
35        foreach ($model->data[$model->alias] as $field => $value)
36        {
37            // Skip if field doesn't exist in schema
38            if (!isset($schema[$field]))
39                continue;
40
41            $fieldSchema = $schema[$field];
42
43            // Check if field is an integer type
44            if (!$this->_isIntegerType($fieldSchema['type']))
45                continue;
46
47            // Handle empty string
48            if ($value === '')
49            {
50                // Convert empty string based on null constraint
51                if (isset($fieldSchema['null']) && $fieldSchema['null'] === true)
52                {
53                    // Nullable column: set to null
54                    $model->data[$model->alias][$field] = null;
55                }
56                else
57                {
58                    // NOT NULL column: set to 0
59                    $model->data[$model->alias][$field] = 0;
60                }
61            }
62            elseif (is_string($value) && !is_numeric($value))
63            {
64                // Handle non-numeric strings
65                // Log warning about non-numeric string
66                CakeLog::warning(sprintf(
67                    'Non-numeric string "%s" provided for integer field %s.%s, converting to 0',
68                    $value,
69                    $model->alias,
70                    $field
71                ));
72
73                // Convert to 0 or null based on null constraint
74                if (isset($fieldSchema['null']) && $fieldSchema['null'] === true)
75                    $model->data[$model->alias][$field] = null;
76                else
77                    $model->data[$model->alias][$field] = 0;
78            }
79        }
80
81        return true;
82    }
83
84    /**
85     * Get schema for model (with caching)
86     *
87     * @param Model $model Model instance
88     * @return array Schema array
89     */
90    protected function _getSchema(Model $model)
91    {
92        $cacheKey = $model->alias;
93
94        if (!isset($this->_schemaCache[$cacheKey]))
95            $this->_schemaCache[$cacheKey] = $model->schema();
96
97        return $this->_schemaCache[$cacheKey];
98    }
99
100    /**
101     * Check if a field type is an integer type
102     *
103     * @param string $type Field type from schema
104     * @return bool True if integer type
105     */
106    protected function _isIntegerType($type)
107    {
108        $integerTypes = ['integer', 'biginteger', 'smallinteger', 'tinyinteger'];
109
110        return in_array($type, $integerTypes);
111    }
112
113}