We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Sanitise all request data at once with data types obtained from models metadata using database introspection srategy?

In fact that was my initial question. But while trying to answer it I realised that I don't understand how database introspection strategy works even after reading docs on it.

Thus I need your help answering my questions:

1)I don't need to set introcpection strategy explicitely in services if I'm not going to cach model's metadata. It will be auto applied. Is that right?

2)Is this correct behaviour that all the fields have string data type in model when I'm getting database record?

object(CRM\Models\InvoiceItem)[98]
  public 'id' => string '4' (length=1)
  public 'invoice_id' => string '1' (length=1)
  public 'part_id' => null
  public 'part_number' => string 'QXXC324' (length=7)
  public 'part_description' => string 'Some vendor crazy stuff server' (length=30)
  public 'price' => string '20' (length=2)
  public 'qty' => string '1' (length=1)
  public 'discount' => string '0' (length=1)
  public 'start_date' => string '2016-02-26 14:37:15' (length=19)
  public 'end_date' => null

As I understand SQL column types are auto applied to model's fields in PDO when saving so It's possibly correct behaviour.

3)How to sanitize all the fields at once if I don't use Phalcon forms (I use custom datagrid).

For instance, I'm getting record for edit and assign it's new values from put array

$editItem = $cart -> getItems(['conditions'=>'id = :ed_id:','bind' => ['ed_id' => $editId]]) -> getFirst ();
$newValuesArr = $this -> request -> getPut();
$editItem -> assign($newValuesArr)

I get this:

object(CRM\Models\InvoiceItem)[98]
  public 'id' => string '4' (length=1)
  public 'invoice_id' => string '1' (length=1)
  public 'part_id' => string '' (length=0)
  public 'part_number' => string 'QXXC324' (length=7)
  public 'part_description' => string 'Some vendor crazy stuff server' (length=30)
  public 'price' => string '20' (length=2)
  public 'qty' => string '5' (length=1)
  public 'points' => string '0' (length=1)
  public 'discount' => string '0' (length=1)
  public 'start_date' => string '2016-02-26 14:37:15' (length=19)
  public 'end_date' => string '' (length=0)

If I will save this record now I will get an error, because part_id has INT type in database.To avoid this I need(as I see it) to sanitize put values before or during the assignment to record. I have a feeling that model's metadata->getDataTypes should be utilized somehow.



9.3k
Accepted
answer
edited Feb '16

ad 1) No, you don't have to set it explicitely, as default Phalcon will cache it to Memory. All info about metadata can be found here https://docs.phalcon.io/en/latest/reference/models-metadata.html

ad 2) This is default behavior, you can change it globally through php.ini file settings

# forces casting bound parameters to specified bind types
db.force_casting = "On"
# allows casting hydrated attributes to the original types in the mapped tables instead of using strings
orm.cast_on_hydrate = "On"

or locally through

\Phalcon\Db::setup(['forceCasting' => true]);
\Phalcon\Mvc\Model::setup(['castOnHydrate' => true]);

More info https://blog.phalcon.io/post/phalcon-2-0-4-released

ad 3) As I know, you can't sanitize automatically with Phalcon. If you are using custom data grid, then extend this grid with bind method, which will utilize models metadata and which will bind and sanitize values to target model depending on their data type.

edited Feb '16

Hi, David. Thanks for your response. With forceCasting enabled I'm getting error when saving end_date apparently because phalcon casts it to string. Also when castOnHydrate enabled assing($array) will replace record's fields data types with string again.

That leaves me no choice other than try to do some validation and set defaults on datagrid's side. It would be nice to have feature to bind dataTypes from model's annotation on assing I think, something like:

$editItem = \CRM\Models\InvoiceItem::findFirst(['conditions'=>'invoice_id = :cart_id: and id = :ed_id:','bind' => ['cart_id'=>$cartId, 'ed_id' => $editId]]);
$dataTypes = $metadata -> getDataTypes($editItem);
$newValuesArr = $this -> request -> getPut();
$editItem -> assign($newValuesArr,$dataTypes);

Small note (not essential). Seems like castOnHydrate setting is working only if models find or findFirst methods were used to get data row. If data row is obtained via relationships (like I did in above example) all fields are cast to string.

Small note (not essential). Seems like castOnHydrate setting is working only if models find or findFirst methods were used to get data row. If data row is obtained via relationships (like I did in above example) all fields are cast to string.

Did you set it in parent and children models? Looks like it has been set only in parent model.

edited Mar '16

Yes, I did. I have:

$this -> belongsTo('invoice_id',  __NAMESPACE__ . '\Invoice', 'id');

For InvoiceItem model and:

$this -> hasMany('id',  __NAMESPACE__ . '\InvoiceItem', 'invoice_id',['alias' => 'Items']);

in invoice model.