I use DBIx::class and postgreSQL.
Imagine table 'Company'. It has fields:
id => {
data_type => 'integer',
is_auto_increment => 1,
},
name => {
data_type => 'text',
},
is_client => {
data_type => 'boolean',
default_value => 'true',
},
When I create new company passing the company name only, create method returns primery key and field I passed: id, name.
How can I get all fields with default values without additional request to DB?
In other words, I want the create method to return all fields with defaults to me.
The DBIx::Class::ResultSet create method returns a DBIx::Class::Result object which has all column values filled.
On databases that support it, DBIx::Class uses RETURNING to get back server populated column values.
See https://metacpan.org/pod/DBIx::Class::ResultSource#retrieve_on_insert how to use that feature.
Related
I'm using Postgres (which I think is related to the problem), and CakePHP 3.
I have the following unit test to just check to make sure that a valid dataset can get saved by the model. When I run the following test, with a standard "bake'd" Model unit test, I get the error below.
I think this is the problem:
We are using fixtures to add some base data. This is the only place that I think might be causing a problem. To add credence to this, while the unit tests were running I ran the following command to get the next auto-incrementing id value and it returned 1, even though it returned the proper number in non-test DB. Select nextval(pg_get_serial_sequence('agencies', 'id')) as new_id;
Unit Test:
public function testValidationDefault()
{
$agencyData = [
'full_name' => 'Agency Full Name',
'mode' => 'transit',
'request_api_class' => 'Rest\Get\Json',
'response_api_class' => 'NextBus\Generic',
'realtime_url_pattern' => 'http://api.example.com',
'routes' => '{"123": {"full_route": "123 Full Route", "route_color": "#123456"}}'
];
$agency = $this->Agencies->newEntity($agencyData);
$saved = $this->Agencies->save($agency);
$this->assertInstanceOf('App\Model\Entity\Agency', $saved);
}
Error:
PDOException: SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "agencies_pkey"
DETAIL: Key (id)=(1) already exists.
Things I've tried
Copied that same code into a controller, and it successfully added the entity in the table.
Adding an id of 200. Same error appears.
Update 1
The fixture for this does have the ID field set each record. Deleting them from the fixture does work, but it breaks other unit tests that rely on some relational data.
I don't like this solution, but adding the following before saving the entity does work.
$this->Agencies->deleteAll('1=1');
[UPDATE: My other answer is the real solution to this problem.! You don't have to do this anymore...]
Here is a less dirty workaround that doesn't require deleting all the records:
use Cake\Datasource\ConnectionManager;
...
$connection = ConnectionManager::get('test');
$results = $connection->execute('ALTER SEQUENCE <tablename>_id_seq RESTART WITH 999999');
//TEST WHICH INSERTS RECORD(s)...
It appears that the auto-incrementing doesn't get properly set/reset during the setUp() or tearDown()... so manually setting it to something really high (greater than the number of existing records) prevents the "duplicate key..." error.
The benefit of this hack (over deleteAll('1=1')) is that you can still subsequently run tests that reference existing DB data.
It might be a problem in your fixture definition. The Cake PHP documentation uses a _constraints field specifying that the id field is a primary key:
'_constraints' => [
'primary' => ['type' => 'primary', 'columns' => ['id']],
]
I believe I've finally figured out the REAL solution to this problem!
I believe this issue stems from a default fixture setting that results from using the bake command to generate fixtures.
When you bake a model it creates the boilerplate for it's fixtures. Notice the autoIncrement for the ID property in the code below? Contrary to what you might think, this should not but true. When I set it to null and remove the ids from the items in the $records array I no longer get uniqueness errors.
public $fields = [
'id' => ['type' => 'integer', 'length' => 10, 'autoIncrement' => true, 'default' => null, 'null' => false, 'comment' => null, 'precision' => null, 'unsigned' => null],
'nickname' => ['type' => 'text', 'length' => null, 'default' => null, 'null' => false, 'comment' => null, 'precision' => null],
...
public $records = [
[
// 'id' => 1,
'nickname' => 'Foo bar',
'width' => 800,
...
The ninja wizards on the CakePHP project are the heroes: source
CakePHP ticket
If id fields are removed from fixture records then they will utilize auto-incrementing when inserted, leaving the table's ID sequence in the right place for inserts that happen during tests. I believe that is why it works for #emersonthis as described above.
That solution has another problem, though: you can't create dependable relationships between fixture records because you don't know what IDs they will get. What do you put in the foreign ID field of a related table? This has led me back to his original solution of just altering the table sequence after records with hard-coded IDs have been inserted. I do it like this in affected TestCases now:
public $fixtures = [
'app.articles',
'app.authors',
];
...
public function setUp()
{
$connection = \Cake\Datasource\ConnectionManager::get('test');
foreach ($this->fixtures as $fixture) {
$tableName = explode('.', $fixture)[1];
$connection->execute("
SELECT setval(
pg_get_serial_sequence('$tableName', 'id'),
(SELECT MAX(id) FROM $tableName)
)");
}
}
This moves the auto-increment sequence to the highest previously-used ID. The next time an ID is generated from the sequence it will be one higher, resolving the problem in all cases.
Including one of these solutions in an upcoming CakePHP release is being discussed here.
Is there any way to access the value of form field 'wklloc_id' in the form field options method of field 'prg_id'?
My Form contains (amongst others) these fields:
has_field 'wklloc_id' => ( type => 'Select', label => 'Winkel(locatie)' );
has_field 'prg_id' => ( type => 'Select', empty_select => 'Kies eerst een Winkel(locatie)', label => 'Productgroep' );
At this point my options method for field 'prg_id' contains:
sub options_prg_id
{
my ($self) = shift;
my (#prg_select_list,$productgroep,$productgroepen);
return unless ($self->schema);
$productgroepen = $self->schema->resultset( 'WinkelLocatieProductgroepen' )->search( {}, { bind => [ 2 ] } );
Is is possible to set the value of the bind variable (i.e. 2) to the value of field 'wklloc_id' and how does one do that? Mind you this is needed before any submit.
The value of a select field is set the same way as other fields, i.e. it comes from an init_object, from parameter values, or from a default. For your case, if you want this field to start with the 'value' 2, then just put: default => 2 in your field definition.
The documentation for HTML::FormHandler::Select says that the sort_column option Sets or returns the column or arrayref of columns used in the foreign class for sorting the options labels. I've tried setting it, but it is not setting my options:
has_field 'my_field' => (
type => 'Select',
sort_column => 'label',
required => 1,
);
I've also tried not setting sort_column, since the default is to sort by the label column and that's what I want, but it doesn't seem to work still. Does anyone know how to have HTML::FormHandler sort the values of my select field? Currently the values are being set with an options function:
sub options_my_field {
return [
{
value => 1,
label => 'One',
},
{
value => 2,
label => 'Two',
},
];
}
I'm using HTML::FormHandler and I would like to have a form that has a dynamic number of form elements. Essentially, I have some inputs that are always present, such as first_name, last_name, and email, but then I have an input, pracitce_area, that I can have many of dynamically (so practice_area1, pracitce_area2, etc). So on the client side I will be using jQuery to dynamically add more practice_area inputs, and I would like for my HTML::FormHandler form to be able to process a dynamic number of these inputs and validate them and put them in the database. The practice_area inputs will be stored in a separate table that will be related with a foreign key to the element of this form, so I would like for HTML::FormHandler to know that these are related and pull out a dynamic number when editing, but also be able to save a dynamic number into the database when saving. Is there a way to handle something like this with HTML::FormHandler? Here's the definition of my form:
package test::Form::Base;
use namespace::autoclean;
use HTML::FormHandler::Moose;
with 'HTML::FormHandler::TraitFor::Model::DBIC';
has title => ( is => 'ro', default => 'Client Information Form');
has '+item_class' => ( default => 'ClientInformationForm' );
has_field 'first_name' => (
type => 'Text',
label => 'First Name',
required => 1,
);
has_field 'last_name' => (
type => 'Text',
label => 'Last Name',
required => 1,
);
has_field 'email' => (
type => 'Email',
label => 'Email',
required => 1,
);
#would like to have this be dynamic in number, and have HTML::FormHandler know
#that it's related with a foreign key when pulling them out of the database
has_field 'practice_area' => (
type => 'TextArea',
);
no HTML::FormHandler::Moose;
__PACKAGE__->meta->make_immutable;
1;
Have you looked at HTML::Formhandler::Repeatable.
You should just be able to use practice_area in your form and have multiple entries. These will just be pulled in an an array(ref) in form processing.
Hi there I have a table in which combination of three fields is unique. I want to put the check of duplication on this combination. Table looks like
I know how to validate single field, But how to validate the combination is not know. To validate one field I use the following function
public function isValid($data) {
// Options for name field validation
$options = array(
'adapter' => Zend_Db_Table::getDefaultAdapter(),
'table' => 'currencies',
'field' => 'name',
'message'=> ('this currency name already exists in our DB'),
);
// Exclude if a id is given (edit action)
if (isset($data['id'])) {
$options['exclude'] = array('field' => 'id', 'value' => $data['id']);
}
// Validate that name is not already in use
$this->getElement('name')
->addValidator('Db_NoRecordExists', false, $options
);
return parent::isValid($data);
}
Will any body guide me how can I validate duplication on combined fields?
There is no ready to use validator for this, as far as I know. You have either to write your own, or do a check with SQL-query with three conditions (one for each field).
you have to Apply a validation on name element of zend form.
Here is code for add validation on name field.
$name->addValidator(
'Db_NoRecordExists',
true,
array(
'table' => 'currencies',
'field' => 'name',
'messages' => array( "recordFound" => "This Currency Name already exists in our DB") ,
)
);
And you must set required true.