how to get rid of primary column in doctrine queries - doctrine-query

Doctrine always includes an ID column in a query, for example:
$new_fees = Doctrine::getTable('Stats')->createQuery('s')
->select('s.sid')->where('s.uid = ?', $this->uid)
->andWhere('s.operation = ?', PPOperationType::FEE_PAID_BY_REFERRED_OWNER)
->andWhere('s.created_at > ?', $lastwd)
->groupBy('s.sid')->execute();
won't work, because s.id is included (which I didn't ask doctrine for). How do I get rid of that id column? Having to use a raw SQL here kills the usefulness of doctrine.

You have to set some column of that table to be the primary in the setTableDefinition, so that doctrine doesn't use default primary as id.
Let's say you sid is you actual primary key.. then...
public function setTableDefinition(){
....
$this->hasColumn('sid', 'decimal', 2, array(
'type' => 'decimal',
'length' => 2,
'unsigned' => 0,
'primary' => true,
'default' => '0',
'notnull' => true,
'autoincrement' => false,
));
}
Notice the 'primary' => true, this prevents doctrine to use id as the default primary key (even when it's not even defined in the table definition file.

This isn't the prettiest solution, but you can call isSubquery(true) on the Doctrine_Query to remove the primary key, in your case s.id.
http://www.doctrine-project.org/api/orm/1.2/doctrine/doctrine_query.html#isSubquery()

Related

How to fix upsert problem when seeding? (laravel)

I have these code below, all seems working but when I try to run unit test it returns an error below.
Here is my seeder (this seeder is called many times in different test cases):
DB::table('sizes')->upsert([
[
'name' => 'jumbo',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
],
[
'name' => 'large',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
]
], ['id'], ['name']);
And the errors pops out:
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: sizes.name (SQL: insert into "sizes" ("created_at", "name", "updated_at") values (2021-05-10 12:52:18, jumbo, 2021-05-10 12:52:18), (2021-05-10 12:52:18, large, 2021-05-10 12:52:18) on conflict ("id") do update set "name" = "excluded"."name")
Here is the migration:
Schema::create('sizes', function (Blueprint $table) {
$table->id();
$table->string('name')
->unique();
$table->timestamps();
});
Your migration will result in such table:
id INT AUTO_INCREMENT PRIMARY_KEY
name VARCHAR UNIQUE
created_at TIMESTAMP
updated_at TIMESTAMP
Your seeder when run first time will insert such records:
id
name
created_at
updated_at
1
jumbo
...
...
2
large
...
...
Now, based on laravel's documentation on upsert:
If you would like to perform multiple "upserts" in a single query, then you should use the upsert method instead.
The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table.
The method's third and final argument is an array of the columns that should be updated if a matching record already exists in the database.
The upsert method will automatically set the created_at and updated_at timestamps if timestamps are enabled on the model:
The important point is:
The method's first argument consists of the values to insert or update,
while the second argument lists the column(s) that uniquely identify records within the associated table.
The method's third and final argument is an array of the columns that should be updated if a matching record already exists in the database
That means, your command:
DB::table('sizes')->upsert([
[
'name' => 'jumbo',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
],
[
'name' => 'large',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
]
], ['id'], ['name']);
Will do this:
check if any record have id of (blank) => no record will match (so upsert will become insert instead)
insert into database, value name=jumbo, and insert into database, value name=large,
this second step will fail since there's already record on database that have name=jumbo (and another record with name=large)
remember that you have name VARCHAR UNIQUE constraint, and this second step violates the UNIQUE constraint
Instead, you should change your seeder into this:
DB::table('sizes')->upsert([
[
'name' => 'jumbo',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
],
[
'name' => 'large',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
]
], ['name'], ['created_at','updated_at']);
The edited version will do this:
check if any record have name of "jumbo"
no record will match initially (so upsert will become insert first time),
and for subsequent run will match (so upsert will become update for subsequent runs)

How to increment column with Laravel Eloquent updateOrCreate()?

Is it possible to return the model id from the request below ?
Bar::insert([
'foo_id' => Foo::updateOrCreate([
'code' => $row[0],
'name' => $row[1]
])->increment('count')->id
]);
I also tried this:
Foo::updateOrCreate([
'code' => $row[0],
'name' => $row[1]
], [
'count' => DB::raw('count + 1')
])->id
But it doesn't work at inserting because it count is not yet set.
HINT: There is a column named "count" in table "foos", but it cannot be referenced from this part of the query. (SQL: insert into "public"."foos" ("id", "name", "count") values (123, Hello, count+1) returning "id")
=== EDIT ===
With DB::raw('IFNULL(count,0) + 1'), I'm getting:
SQLSTATE[42703]: Undefined column: 7 ERROR: column "count" does not exist
LINE 1: ... ("code", "name", "count") values ($1, $2, IFNULL(count,0) +...
^
HINT: There is a column named "count" in table "foos", but it cannot be referenced from this part of the query. (SQL: insert into "public"."foos" ("code", "name", "count") values (123, Hello, IFNULL(count,0) + 1) returning "id")
Unfortunately I don't think this is possible to reference the count column only when the update happens (since this is what is happening here). You will need to take the roundabout way:
$model = Foo::firstOrNew([
'code' => $row[0],
'name' => $row[1]
]);
$model->count = ($model->exists ? $model->count : 0) + 1;
$model->save();
Here $model->exists will be true if the model was retrieved from the database or false if a new one was created.
Efficiency-wise firstOrCreate is what updateOrCreate does anyway so there's no query cost added
try using IFNULL
Foo::updateOrCreate([
'code' => $row[0],
'name' => $row[1]
], [
'count' => DB::raw('IFNULL(count,0) + 1')
])
if you want the id, try it like this:
$id=(Foo::updateOrCreate([
'code' => $row[0],
'name' => $row[1]
], [
'count' => DB::raw('IFNULL(count,0) + 1')
]))->id;
note: this is working on mysql, not PostgreSQL
you just need to set column auto-increment in database and not need set it column in your eloquent

How can i get last inserted row By using Laravel DB::table()->insert()?

I user this code for insert data .
$conv = DB::table('conversations')
->insert([
'is_seen' => $other_user_id,
'user_one' => $user_id,
'user_two' => $other_user_id,
'user_one_status' => 1,
'user_two_status' => 0,
'message_status' => 0,
'last_message' => $messageCon
]);
Its return true false value. I need last inserted row.
Assuming there's a reason you're not using Eloquent, you could use insertGetId.
$conv = DB::table('conversations')
->insertGetId([
'is_seen' => $other_user_id,
'user_one' => $user_id,
'user_two' => $other_user_id,
'user_one_status' => 1,
'user_two_status' => 0,
'message_status' => 0,
'last_message' => $messageCon
]);
Caveats from the documentation:
Auto-Incrementing IDs
If the table has an auto-incrementing id, use the insertGetId method to insert a record and then retrieve the ID:
When using PostgreSQL the insertGetId method expects the auto-incrementing column to be named id. If you would like to retrieve the ID from a different "sequence", you may pass the column name as the second parameter to the insertGetId method.
Insert cant return whole row.
You can change it to insertGetId and then select it from database by that id.
Or use models, and use create method (Conversation::create([...]))

Magento 1.7 - add attribute to order_item and setting value

I want to add a new attribute to order_item
install-0.1.0.php
$installer->addAttribute('order_item', 'xxx', array('type'=>'text', 'visible' => true, 'required' => false, 'is_user_defined' => true, 'note' => 'Field comment'));
$installer->addAttribute('quote_item', 'xxx', array('type'=>'text', 'visible' => true, 'required' => false, 'is_user_defined' => true, 'note' => 'Field comment'));
I see 2 new rows in eav_attribute
Running this two times prints nothing:
$item = Mage::getModel('sales/order_item');
$item->load(91);
$item->setXxx("test");
$item->setData("xxx", "test");
print $item->getXxx();
$item->save();
How do I set and get a value in an order item for this attribute?
Unfortunately sales/order and sales/order_item does not inherit or use the eav_attribute structure in Magento. This blog post explains it pretty well: http://www.krilion.net/blog/2012/08/adding-a-custom-attribute-to-a-magento-order/
The jist of it is that you'll need to create your installer script to create a new column in the flat table (sales_flat_order_item) in order to save your new value. You should be able to save your xxx value as soon as there's an xxx column in the flat table. This means your installer script needs to extend Mage_Sales_Model_Mysql4_Setup and not Mage_Eav_Model_Entity_Setup.

Add an auto_increment column in Magento setup script without using SQL

Previously I asked how to ALTER TABLE in Magento setup script without using SQL. There, Ivan gave an excellent answer which I still refer to even now.
However I have yet to discover how to use Varien_Db_Ddl_Table::addColumn() to specify an auto_increment column. I think it has something to do with an option called identity but so far have had no luck.
Is this even possible or is that functionality incomplete?
One can create an autoincrement column like that (at least since Magento 1.6, maybe even earlier):
/** #var $table Varien_Db_Ddl_Table */
$table->addColumn( 'id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
'auto_increment' => true,
'unsigned' => true,
'nullable' => false,
'primary' => true,
), 'ID' );
Instead of "auto_increment", one may also use the keyword "identity".
I think that's something that hasn't been implemented yet.
If you look at the source to addColumn, you can see it looks for a identity/auto_increment option and sets an IDENTITY attribute on the internal column representation.
#File: lib/Varien/Db/Ddl/Table.php
if (!empty($options['identity']) || !empty($options['auto_increment'])) {
$identity = true;
}
$upperName = strtoupper($name);
$this->_columns[$upperName] = array(
'COLUMN_NAME' => $name,
'COLUMN_TYPE' => $type,
'COLUMN_POSITION' => $position,
'DATA_TYPE' => $type,
'DEFAULT' => $default,
'NULLABLE' => $nullable,
'LENGTH' => $length,
'SCALE' => $scale,
'PRECISION' => $precision,
'UNSIGNED' => $unsigned,
'PRIMARY' => $primary,
'PRIMARY_POSITION' => $primaryPosition,
'IDENTITY' => $identity
);
However, if you look at the createTable method on the connection object
#File: lib/Varien/Db/Adapter/Pdo/Mysql.php
public function createTable(Varien_Db_Ddl_Table $table)
{
$sqlFragment = array_merge(
$this->_getColumnsDefinition($table),
$this->_getIndexesDefinition($table),
$this->_getForeignKeysDefinition($table)
);
$tableOptions = $this->_getOptionsDefination($table);
$sql = sprintf("CREATE TABLE %s (\n%s\n) %s",
$this->quoteIdentifier($table->getName()),
implode(",\n", $sqlFragment),
implode(" ", $tableOptions));
return $this->query($sql);
}
you can see _getColumnsDefinition, _getIndexesDefinition, and _getForeignKeysDefinition are used to create a CREATE SQL fragment. None of these methods make any reference to identity or auto_increment, nor do they appear to generate any sql that would create an auto increment.
The only possible candidates in this class are
/**
* Autoincrement for bind value
*
* #var int
*/
protected $_bindIncrement = 0;
which is used to control the increment number for a PDO bound parameter (nothing to do with auto_increment).
There's also a mention of auto_increment here
protected function _getOptionsDefination(Varien_Db_Ddl_Table $table)
{
$definition = array();
$tableProps = array(
'type' => 'ENGINE=%s',
'checksum' => 'CHECKSUM=%d',
'auto_increment' => 'AUTO_INCREMENT=%d',
'avg_row_length' => 'AVG_ROW_LENGTH=%d',
'comment' => 'COMMENT=\'%s\'',
'max_rows' => 'MAX_ROWS=%d',
'min_rows' => 'MIN_ROWS=%d',
'delay_key_write' => 'DELAY_KEY_WRITE=%d',
'row_format' => 'row_format=%s',
'charset' => 'charset=%s',
'collate' => 'COLLATE=%s'
);
foreach ($tableProps as $key => $mask) {
$v = $table->getOption($key);
if (!is_null($v)) {
$definition[] = sprintf($mask, $v);
}
}
return $definition;
}
but this is used to process options set on the table. This auto_increment controls the table AUTO_INCREMENT options, which can be used to control which integer an AUTO_INCREMENT starts at.

Resources