symfony doctrine will not default a boolean to 0 - mysql

I have tried in several ways to have symfony default a boolean to 0 rather than null (as null gives me a database level error upon flush).
An exception occurred while executing a query: SQLSTATE[23000]:
Integrity constraint violation: 1048 Column 'auto_created' cannot be
null
This made no difference:
/**
* #ORM\Column(type="boolean", options={"default":"0"})
*/
private $autoCreated;
Some logic i the setter made no difference either
public function setAutoCreated(bool $autoCreated): self
{
if is_null($autoCreated) {
$autoCreated = 0;
}
$this->autoCreated = $autoCreated;
return $this;
}
As well as
public function setAutoCreated(bool $autoCreated): self
{
if is_null($autoCreated) {
$autoCreated = false;
}
$this->autoCreated = $autoCreated;
return $this;
}
Database looks like this
I am clearly missing something...?
Sure I can do a simple $user->setAutoCreated(false); everywhere I create this entity, but I don't get why I should have to 😎

Depending on the version of PHP you're using, you should be able to do something like this in your entity class:
/**
* #ORM\Column(type="boolean")
*/
private $autoCreated = false;
Whenever an instance of this class is created, $autoCreated will be set to false. So when you try to persist the object, it'll have a default value of false and Doctrine will set the field to 0.
Alternatively, you can explicitly set $autoCreated to false in your constructor:
public function __construct()
{
$this->autoCreated = false;
}
Note that you can only use the first approach for simple, built-in PHP types or constants. For more complicated objects (e.g., a Doctrine ArrayCollection) you'll need to use the constructor approach.

Related

Doctrine: addSelect method creates undesired seperate object (Symfony)

I want to query the entity based on a calculated property that does not exist in the database or on the entity.
If I run
return $this->createQueryBuilder('b')
->select('b')
->addSelect(
'... as extra_property'
)
->having('extra_property = :param')
->setParameter('param', $param)
->orderBy('extra_property', 'ASC')
->getQuery()
->getResult();
This results in a collection with each entity in the following format:
"0": {}, // The 9 entity properties
"extra_property": "value"
However, I want the extra_property to be added to the other entity properties as the tenth property. How do I fix this?
The problem lies in the getResult() method, the default hydration method is "HYDRATE_OBJECT" which will try to hydrate the output as the defined entity, which does not know about your extra property.
public function getResult($hydrationMode = self::HYDRATE_OBJECT)
{
return $this->execute(null, $hydrationMode);
}
Depending on what your other properties are, you could just use the "HYDRATE_SCALAR" option which will just give you a flat output of the results.
If you other properties are nested entities, you will have to manually select the fields in your select to bypass the hydration process, or find a way to add that property to your entity and tell the hydrator to get that data from a dedicated query or something.
You can find the different hydration methods as constants of the AbstractQuery class, you can read more on the Doctrine documentation
/**
* Hydrates an object graph. This is the default behavior.
*/
public const HYDRATE_OBJECT = 1;
/**
* Hydrates an array graph.
*/
public const HYDRATE_ARRAY = 2;
/**
* Hydrates a flat, rectangular result set with scalar values.
*/
public const HYDRATE_SCALAR = 3;
/**
* Hydrates a single scalar value.
*/
public const HYDRATE_SINGLE_SCALAR = 4;
/**
* Very simple object hydrator (optimized for performance).
*/
public const HYDRATE_SIMPLEOBJECT = 5;
/**
* Hydrates scalar column value.
*/
public const HYDRATE_SCALAR_COLUMN = 6;

Doctrine findBy boolean field returns no results

Recently, a piece of code stopped working. I haven't made any changes to it so I don't know why.
Here's the code:
$invites = $this->vault_em->getRepository('AppBundle:Invite\LocalInvite')->findBy([
'active' => true,
]);
Now, it's returning an empty array, even though there are LocalInvite records with active = 1.
Here are the doctrine mappings:
/**
* #ORM\Entity
* #ORM\Table(name="invite")
*/
class LocalInvite extends Invite {
//...
}
/** #ORM\MappedSuperclass */
abstract class Invite implements \JsonSerializable {
/** #ORM\Column(type="boolean", options={"default": true}) */
protected $active;
//...
}
To debug, I copied the underlying MySQL query that Doctrine is executing from the debug logs:
SELECT t0.id AS id_1, t0.email AS email_2, t0.active AS active_3, t0.location AS location_4, t0.cohort_leadership AS cohort_leadership_5, t0.timezone AS timezone_6, t0.date_record_created AS date_record_created_7, t0.date_record_deleted AS date_record_deleted_8, t0.date_restart AS date_restart_9, t0.date_start_invite AS date_start_invite_10, t0.employee_id AS employee_id_11, t0.first_name AS first_name_12, t0.corporate_client_name AS corporate_client_name_13, t0.client_id AS client_id_14, t0.landing_page_url AS landing_page_url_15, t0.user_id AS user_id_16, t0.recipient_id AS recipient_id_17 FROM invite t0 WHERE t0.active = true;
When I plug that query into a MySQL IDE, it returns results.
Why does the findBy return no results?
try to change 'AppBundle:Invite\LocalInvite' by LocalInvite::class

Laravel Schema Default Value Function

I want the default value for my Server model's sid to be the uniqid() function to be run every time. Something like this, for example
$table->string('sid')->default(uniqid);
How can I achieve this result?
You can use an event listener to set the sid attribute on your model. You can do this using an event listener, model observer, or just a closure function inside your model's boot function.
// app\Models\YourModel.php
/**
* Define model event callbacks.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->sid = uniqid();
});
}
Unfortunately no, MySQL requires constants for the default. So, the default value must be a constant, it cannot be a function or an expression.
Only way is to allow the fields nullable and add uniqid() while creating records on the database like this:
$table->string('sid')->nullable();
Now, When you add records set sid value to uinqid() value
If you have model named Table then
$record = new Table();
$record-> -----
--------
--------
$record->sid = uniqid();
$record->save();
This is the way you can achieve.
UPDATE
You can set default value in model as well like this:
protected $attributes = array(
'sid' => uniqid(),
);
Hope you understand.
i think this is the easiest way
$table->unique('sid')->index()

Yii2 - replacement for beforeFind to optimize nested views in MySql

Yii1 used to have beforeFind method in which you could modify the query or whatever else you might want to do.
In Yii2 the suggested alternative is to use the modelQuery solution for example
class MyModel extends \yii\db\ActiveRecord
{
public static function find()
{
return new MyModelQuery(get_called_class());
}
/* ... */
}
and
class MyModelQuery extends \yii\db\ActiveQuery
{
public function init( )
{
/** do something here **/
}
}
But how do I pass or reference MyModel within MyModelQuery?
For example:-
class MyModelQuery extends \yii\db\ActiveQuery
{
public function init( )
{
$sql = "SET #variable = {$MyModel->variable1}";
}
}
EDIT
For completeness, I've added a use case to help others in future.
I have nested views with group by's running under MySql and it runs VERY badly.
In my case, I have orders, order-items and order-item-fees tables, each one-to-many to the next and I want to sum the order totals. I have nested view, one at each level to sum to the level above, but at the order-item and order-item-fee levels MySql is grouping the whole table first (I cannot use algorithm=merge as I have a GROUP BY).
I'm implementing the Pushdown method where you define a SQL variable to use in sub-views to narrow down the search as outlined here: http://code.openark.org/blog/mysql/views-better-performance-with-condition-pushdown
and also here
https://www.percona.com/blog/2010/05/19/a-workaround-for-the-performance-problems-of-temptable-views/
In this way, if I can add a 'WHERE order_id=' to the where clause of the two sub-views, I reduce a 3.5 second query down to 0.003 second query.
So using, Salem's suggestion below, I can execute a SQL statement 'SET #order_id=1234' before my query, which is then picked up in the order-item and order-item-fee views using a function. Note: this is connection specific, so no danger of collisions between sessions.
A bit convoluted but fast.
It would be interesting, though, to see a performance comparison between SQL and looping in PHP perhaps....
EDIT 2
In fact, you normally use find() as a static method, so there is no way of using $this->order_id, so I changed this to over-ride the findOne method
public static function findOne( $orderId )
{
if ( isset($orderId) )
{
$sql = "SET #orderId='{$orderId}'";
Yii::$app->db->createCommand($sql)->execute();
}
return parent::findOne( $orderId );
}
I also use this view with other searches, so in the view I need to check whether the orderId is set or not ...
where (
CASE
WHEN ( NOT isnull( get_session_orderId() ) )
THEN `order`.order_id = get_session_cartref()
ELSE `order`.order_id LIKE '%'
END
)
About how to involve an ActiveQuery class check my answer here:
Yii2 : ActiveQuery Example and what is the reason to generate ActiveQuery class separately in Gii?
But if what you are trying to do doesn't require building named scopes then you may simply override the find method by something like this:
public static function find()
{
return parent::find()->where(['variable' => 'some value']);
}

Calling stored procedure in codeigniter

I am using latest codeigniter and trying to call stored procedure from my model. Also I am using mysqli as database driver. Now I am having an error when I call two stored procedures. Following is the error:
Error Number: 2014
Commands out of sync; you can't run this command now
call uspTest();
Filename: E:\wamp\www\reonomy-dev\system\database\DB_driver.php
Line Number: 330
Note that when I call a single stored procedure it works fine. Here is the code for model.
class Menus_model extends CI_Model {
function __construct()
{
parent::__construct();
}
public function getMenus()
{
$query = $this->db->query("call uspGetMenus()");
return $query->result();
}
public function getSubMenus()
{
$query = $this->db->query("call uspTest()");
return $query->result();
}
}
Here is the code from controller
class MYHQ extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('menus_model');
}
public function index()
{
$menu = $this->menus_model->getMenus();
$submenu = $this->menus_model->getSubMenus();
}
}
Is there any solution without hacking the core of codeigniter??
I follow the blog of Mr. Tim Brownlaw:
http://ellislab.com/forums/viewthread/73714/#562711
First, modify application/config/config.php, line 55.
$db['default']['dbdriver'] = 'mysqli'; // USE mysqli
Then, add the following into mysqli_result.php that is missing this command for some strange reason (under /system/database/drivers/mysqli/mysqli_result.php).
/**
* Read the next result
*
* #return null
*/
function next_result()
{
if (is_object($this->conn_id))
{
return mysqli_next_result($this->conn_id);
}
}
Then, in your model, add $result->next_result().
Below is my example.
function list_sample($str_where, $str_order, $str_limit)
{
$qry_res = $this->db->query("CALL rt_sample_list('{$str_where}', '{$str_order}', '{$str_limit}');");
$res = $qry_res->result();
$qry_res->next_result(); // Dump the extra resultset.
$qry_res->free_result(); // Does what it says.
return $res;
}
Having the same problem I found another approach which doesn't change the core, but instead uses a small helper.
Edit: The below linked asset is nowhere to be found.
See CoreyLoose post.
https://ellislab.com/forums/viewthread/71141/#663206
I had to make a small adjusment to his helper though. The line
if( get_class($result) == 'mysqli_stmt' )
could possibly produce a warning since the $result sometimes is passed as a boolean. I just put a check prior to this line and now it works perfectly, with no tinkering with the core!
This seems to be a bug in CodeIgniter. How come it's still in there is beyond me.
However, there's a couple of ways to overcome it.
Check here: http://codeigniter.com/forums/viewthread/73714/
Basically, you modify mysqli_result.php to include next_result() function and make sure to call it after every stored proc. call.
Just note that it assumes you're using mysqli as your DB driver... but you can probably do something similar with any other. You can change your driver in /application/config/database.php It's the line that says
$db['default']['dbdriver'] = 'mysql';
by default. Change it to:
$db['default']['dbdriver'] = 'mysqli';
You could also just close/reopen a DB connection between the calls, but I would definitely advise against that approach.
change dbdriver to "mysqli"
put this function to your model and use it to call stored procedure
function call( $procedure )
{
$result = #$this->db->conn_id->query( $procedure );
while ( $this->db->conn_id->next_result() )
{
//free each result.
$not_used_result = $this->db->conn_id->use_result();
if ( $not_used_result instanceof mysqli_result )
{
$not_used_result->free();
}
}
return $result;
}