Not the usual WordPress 'NOT EXISTS' meta query - mysql

I've encountered an issue where "not exists" meta queries do not work as expected if there are no metadata at all on the particular post. Something like...
add_filter('pre_get_posts' , 'my_pre_get_posts' );
function my_pre_get_posts($query) {
$query->set('meta_query', array(
'relation' => 'OR',
array(
'key' => 'mymetakey',
'value' => '',
'compare' => 'NOT EXISTS'
),
array(
'key' => 'mymetakey',
'value' => '1',
'compare' => '!='
),
)
);
}
This works fine, as expected, when the post has some metadata.. any other metadata. Just hitting the editor fixes this because of the '_edit_lock' meta. However the autogenerated woocommerce pages do not have this, and out of the box - this query does not work.
Presumably this is an idiosyncrasy of the way the table joins work. Anybody know a good workaround - other than writing some dummy meta data into each post.

Problem goes away upon upgrading to WordPress 4.1. I should've waited 1 day for the release!

Related

Seeding NULL values into NOT NULL columns with CI4

I'm having a problem with the CodeIgniter 4 seeder that I can't find any solution to so far. I used CodeIgniter 4 migrations to generate my tables and by default the columns are NOT NULL. There were no issues seeding the db in MySQL, even though I only inserted in some columns, which would have made anything else NULL.
The problem is that if I change to PostgreSQL, the migrations run fine. I just needed to change DOUBLE to NUMERIC. The seeder was not so easy. The first error I get is Message: pg_query(): Query failed: ERROR: null value in column "created_at" violates not-null constraint which is weird, since when using MySQL those columns are filled normally. But even if I manually tell my migrations to fill create_at and updated_at, I keep getting this error in any other columns that are NOT NULL, while in MySQL they were simply left empty.
I think I'm misunderstanding something with CI4, MySQL or PostgreSQL in this situation, and would be glad if someone could shed some light.
So to sum this up, I tried seeding a DB using PostgreSQL, which worked fine in MySQL, and it wouldn't let me not insert any value in the dozens of other fields not specified in the seeder. And I'm worried this will be a problem in forms and this sort of thing, since some fields not being filled will result in the same error.
For some clarification, here is the migration for a table in my DB:
class Configuracoes extends Migration
{
public function up()
{
$this->forge->addField([
'id_config' => [
'type' => 'INT',
'constraint' => 9,
'usigned' => true,
'auto_increment' => true,
],
'nome_do_app' => [
'type' => 'VARCHAR',
'constraint' => 128
],
'tema' => [
'type' => 'INT'
],
'xNome' => [
'type' => 'VARCHAR',
'constraint' => 128
],
'xFant' => [
'type' => 'VARCHAR',
'constraint' => 128
],
'CNPJ' => [
'type' => 'VARCHAR',
'constraint' => 14
],
'telefone' => [
'type' => 'VARCHAR',
'constraint' => 11
],
'endereco' => [
'type' => 'VARCHAR',
'constraint' => 256
],
'arquivo-imagem-de-fundo-login' => [
'type' => 'VARCHAR',
'constraint' => 128
],
'logomarca' => [
'type' => 'VARCHAR',
'constraint' => 128
],
'created_at' => [
'type' => 'DATETIME'
],
'updated_at' => [
'type' => 'DATETIME'
],
'deleted_at' => [
'type' => 'DATETIME'
]
]);
$this->forge->addKey('id_config', true);
$this->forge->createTable('configuracoes');
}
And the Seeder for this table:
$this->db->table('configuracoes')->insert([
'nome_do_app' => 'App',
'tema' => '4',
'xNome' => 'name',
'xFant' => 'name',
'CNPJ' => '0000000000000',
'telefone' => '(000) 0000-0000',
'endereco' => 'adress'
]);
You can see the seeder is only filling some columns, so running it I will get the same error as before, asking for each column missing. I can aways fill the missing columns with blank spaces and it will run just fine, but I need to understand why it's happening and if it will interfere with the POST methods and sorts in my code too.
I learned a thing or two trying to figure this problem out, so migh as well share with anyone interested. Feel free to correct me if I say anything wrong, I will gladly edit my answer.
MySQL and PostgreSQL have a few differences dealing with the NOT NULL constraint, and my code was not considering those differences.
While trying to seed a MySQL database with let's say, this code:
$this->db->table('configuracoes')->insert([
'nome_do_app' => 'App',
'tema' => '4',
'xNome' => 'name',
'xFant' => 'name',
'CNPJ' => '0000000000000',
'telefone' => '(000) 0000-0000',
'endereco' => 'adress'
]);
Anything else left out the seeder will be given a NULL value, and MySQL won't complain about it, even if the fields are defined as NOT NULL. While I'm not sure how it works in depth, this is what I could see during the time trying to resolve this issue.
Another thing that's different about PostgreSQL compared with MySQL, is the date field type. While leaving this field NULL in a MySQL database, it would automatically set it to 0000-00-00 00:00:00:000. But PostgreSQL won't accept such value, neither NULL, of course. Unless you tell Postgre to accept a NULL value in that field, that would be just an empty field like the others. My code wasn't taking this change in consideration either.
With PostgreSQL, if a field is set to NOT NULL, you can't simply tell Postgre to insert a NULL value, not even manually do so. And my entire database was set to NOT NULL, because it would never be a problem to MySQL.
The solution: I reviewed all my migrations, and only left NOT NULL in the fields that actually needed a value no matter what. Then while seeding the table, I took the NOT NULL fields in consideration and manually inserted a default value, like a space or a zero.
Thank you, everyone that tried to help!
PSA: I'm not the one who created this database from the start, I'm just mainteining and continuing a project made by someone else. So I can't really say anything about the choices made to structure it that way.

WordPress query posts without ACF repeater subfields AND specific value

I have two custom post types, playlists and games. Playlists have a ACF repeater field, that contain games.
My goal: I am trying to build a simple query, that gets all playlists that DO NOT have this game listed in the repeater field AND alll playlists that are empty (do not have any repeater fields / database entries at all). I want to use the get_posts() function to achieve my goal, if possible.
My problem: I can only get the query to show ONLY the playlists that HAVE the game by using the = operator in the "game_filter" meta query compare field (but I want the opposite). If I choose the != or <> or IS NOT operator, it spits out all available posts. And if I also include my "empty_playlists" meta query, it ALWAYS shows me all playlists, no matter what operators I use, even tho it is a OR and the listed posts do have entrys.
I tried a lot but just cant get it to work. All exmaples I found were about different things, mostly about the % problem with the repeater field names and its solution. I hope someone can help me with this real quick. What am I doing wrong? Please help fellow and better coders! :-)
This is my code
$excluded_playlists_game_id = 274;
$user_id = 1;
$args = array(
'post_type' => PSiCorePostTypes::PLAYLISTS,
'author' => $user_id,
'order_by' => 'title',
'order' => 'ASC',
'suppress_filters' => false,
'meta_query' => array(
'relation' => 'OR',
'game_filter' => array(
'type' => 'NUMERIC',
'key' => 'psi_playlist_games_%_item',
'compare' => '!=',
'value' => $excluded_playlists_game_id,
),
'empty_playlists' => array(
'key' => 'psi_playlist_games_%_item',
'compare' => 'NOT EXISTS',
),
),
);
$user_playlists = get_posts( $args );
and this function for the % problem.
add_filter( 'posts_where', 'get_user_playlists_query_allow_wildcard' );
function get_user_playlists_query_allow_wildcard( $where ) {
global $wpdb;
$where = str_replace(
"meta_key = 'psi_playlist_games_%_item",
"meta_key LIKE 'psi_playlist_games_%_item",
$wpdb->remove_placeholder_escape( $where )
);
return $where;
}

How to solve more complex wp_query meta_query with WP title

If I were using a SQL statement, this is what it would look like:
select * from courses where active = 1 AND (title = $string OR description = $string)
'courses' is a custom post type
'active' is a custom field created with ACF
'description' is a custom field created with ACF
'title' is the standard WP title for posts
This is what I have so far:
$args_courses = array(
'post_type' => 'courses',
'posts_per_page' => 9999,
'meta_query' => array(
array(
'key' => 'active',
'value' => 1,
'compare' => '='
),
'relation' => 'AND',
array(
'key' => 'description',
'value' => $string,
'compare' => '='
),
'relation' => 'OR',
array(
'key' => 'title',
'value' => $string,
'compare' => '='
)
),
);
It seems as though I can't access the WP title through a meta_query, but can't figure out how to turn my query around so 'active' remains the first column to check on.
Thanks in advance for any help!
What you are trying to do is not supported by the default WP_Query functionality, however you can extend it as described in the following answer from the WordPress Stackexchange site. Note that you you may need to alter it further for your specific use case.
https://wordpress.stackexchange.com/questions/178484/wp-query-args-title-or-meta-value
A few notes on your current query.
relation should only be used once in your meta_query. WordPress uses arrays to do the grouping, and wouldn't know to use "AND" or "OR" in your example.
It looks like you want "all" posts, not really 9999 posts, so you should use -1 to fetch everything in one result set.

Newly added field to table is completely ignored

I am completely new to Drupal. I inherited a very ugly and incorrect code, unfortunately. In fact I would like to implement a proper login-with-facebook feature, which was totally mis-implemented. It tried to identify users by their email address, however, for some reason, upon login with Facebook, users logged in with the wrong user. I would like to identify the user based on Facebook ID, however, there was no column for that purpose in the database.
As a result, I have implemented a small script, which added a facebook_id and a facebook_token to the table representing the users. However, these new columns are not seen by the drupal_get_schema function in bootstrap.
If I do this:
$schema = drupal_get_schema("users");
echo var_dump($schema["fields"]);
It shows the fields except the two newly created fields. This way a SchemaCache object is initialized. I assumed that the schema might be cached. So I tried something different:
$schema = drupal_get_schema("users", true);
echo var_dump($schema["fields"]);
to make sure that drupal_get_complete_schema(true) will be called. However, the fields are not seen this way either. Is there a way I can tell Drupal to acknowledge the existence of the two newly created columns? If not: what should I do? Should I remove the two columns from the database table and use db_add_field("users", "facebook_id") and db_add_field("users", "facebook_token") respectively? If so, where should I call these?
Sorry if the question is too simple or I am misunderstanding these technologies, but I have tried to solve this for hours and I am at a loss, because this is my first drupal/bootstrap project and the source-code using these does not help me at all.
EDIT:
Since, at the time of this writing I have not received any answers apart from a tool recommendation which did not address my question, I have continued my research in the area. I removed the columns from the database to create them in a Drupal way. I have implemented this function in user.module:
function user_schema_alter() {
db_add_field('users', 'facebook_id', array(
'type' => 'varchar', //was initially a bigint, but Drupal generated a query which always crashed
'length' => 20,
'not null' => TRUE,
'default' => ".", //was initially -1, but Drupal generated a query which always crashed
));
db_add_field('users', 'facebook_token', array(
'type' => 'varchar',
'length' => 300,
'not null' => TRUE,
'default' => 'unavailable',
));
}
and I invoke it from altconnect.module, like this:
$schema = drupal_get_schema("users");
if (!isset($schema["fields"]["facebook_id"])) {
user_schema_alter();
}
It creates the columns, but later the existence of those columns will not be known about and subsequently an error will be thrown as the code will try to re-create them. Besides the fact that I had lost a lot of time until I realized that Drupal is unable to support bigint fields having -1 as their default value I had to conclude that with this solution I am exactly in the same situation as I were initially, with the difference that with this Drupal solution I will always get an exception if the columns already exist, because the schema will not be aware of them and subsequently, the code will always enter that if.
I fail to understand why is this so difficult in Drupal and I totally fail to understand why trying
db_add_field('users', 'facebook_id', array(
'type' => 'bigint',
'length' => 20,
'not null' => TRUE,
'default' => -1,
));
throws an exception due to syntax error. Maybe I should just leave this project and tell anyone who considers using Drupal to reconsider :)
I was able to find out what the answer is, at least for Drupal 6.
In user.install we need to do the following:
//...
function user_schema() {
//...
$schema['users'] = array(
//...
'fields' => array(
//...
'facebook_id' => array(
'type' => 'varchar',
'length' => 20,
'not null' => TRUE,
'default' => ".",
),
'facebook_token' => array(
'type' => 'varchar',
'length' => 300,
'not null' => TRUE,
'default' => 'unavailable',
),
//...
),
//...
}
//...
/**
* Adds two fields (the number is some kind of version number, should be the biggest so far for the module)
*/
function user_update_7919() {
db_add_field('users', 'facebook_id', array(
'type' => 'varchar',
'length' => 20,
'not null' => TRUE,
'default' => ".",
));
db_add_field('users', 'facebook_token', array(
'type' => 'varchar',
'length' => 300,
'not null' => TRUE,
'default' => 'unavailable',
));
}
When this is done, log in with the admin user and go to http://example.com/update.php
There you will see the thing to be updated. Run it. If you wonder why do we have to do all this, why don't we run some scripts directly, then the answer is that this is how Drupal operates. It simplifies your life by making it complicated, but do not worry, while you wait for update.php to do the updates which would take less than a second if it was your script, you can ponder about the meaning of life, quantum-mechanics or you can try to find out the reason this is so over-complicated in Drupal and you can go out for a walk. When you focus again, if you are lucky, update.php has completed its job and the two columns should be in the database.

CakePHP 1.3 not saving to database but sql statement is correct and insertID is increased correctly

I already searched many forums for my really strange issue, but I still can't figure out whats going wrong during my save process... The issue: Cake says, my data was saved, creates an autoincrement-ID but no record is stored in the database.
The environment
I have a cake-1.3.13 app running for some time and now needed to add another database table, which is of course related to other tables. My problem is saving records for the habtm-relation table, which looks like this:
CREATE TABLE IF NOT EXISTS `employees_projects_rejectreasons` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`employees_project_id` int(10) unsigned NOT NULL,
`rejectreason_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `employees_project_id` (`employees_project_id`,`rejectreason_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=6;
I scaffolded the simple model only with basic validation criteria.
<?php
class EmployeesProjectsRejectreason extends AppModel {
var $name = 'EmployeesProjectsRejectreason';
var $validate = array(
'employees_project_id' => array(
'numeric' => array(
'rule' => array('numeric'),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'rejectreason_id' => array(
'numeric' => array(
'rule' => array('numeric'),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'EmployeesProject' => array(
'className' => 'EmployeesProject',
'foreignKey' => 'employees_project_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Rejectreason' => array(
'className' => 'Rejectreason',
'foreignKey' => 'rejectreason_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
I created several records for Rejectreasons and EmployeesProjects, so I have some valid entries here in the database. Now I want to link them together by creating a new record in the given employees_projects_rejectreasons table. I try to do this from another controller (the EmployeesProjectsController). Here is my latest attempt to save the data:
$this->EmployeesProject->EmployeesProjectsRejectreason->create();
$eprData = array(
'EmployeesProjectsRejectreason' => array(
'employees_project_id' => (int)$id,
'rejectreason_id' => (int)$rrId
)
);
if($this->EmployeesProject->EmployeesProjectsRejectreason->save($eprData)) {
debug('successfully saved EPR with ID '.$this->EmployeesProject->EmployeesProjectsRejectreason->__insertID);
} else {
debug('could not save EPR with employees_project_id='.$id.' and rejectreason_id='.$rrId);
}
Now what happens
After I make an attempt to save a record, my debug gives me the following success report:
successfully saved EPR with ID 4
So the save() call returned true, a new ID was created by the auto_increment function of mySQL. So far so good. But when I check my database, there was no record created. But the auto_increment_counter was increased by 1, as if a record was stored, but it wasn't.
Running the app with debug-level 2, I can see the generated SQL-statement from cake, which looks perfectly fine to me:
INSERT INTO `employees_projects_rejectreasons` (`employees_project_id`, `rejectreason_id`) VALUES (3, 3)
If I run this statement directly on the sql server, the record ist inserted correctly.
What I already tried
I already tried different approaches with the save procedure. I tried working with setters instead of a data-array:
$this->EmployeesProject->EmployeesProjectsRejectreason->set('employees_project_id', $id);
as well, but it made no difference. After I wrote a custom save-method in the EmployeesProjectsRejectreason-Model, calling it from the controller, but it always produced the same result.
I tried
deleting the model-cache
restarting the server-instances and the server itself
Deleting the table and creating it again
disabling validation in the model
removing the unique foreign-key index
Saving with hard-coded and existing ids as foreign key
Some more strange behaviour
The last tests with hard-coded IDs in my controller code confronted me with more riddles: If I try storing existent foreign_key-IDs, the data is not saved as before. But if both IDs are hardcoded and NOT EXISTING (I used invented IDs 345 AND 567, which are definetely not existing in the database) a record was finally inserted!
Moreover I scaffolded Models, Views and Controllers for the new tables. When I run the scaffolded view "myApp/employees_projects_rejectreasons/add" and add a new record, everything works just fine.
I'm just not able to save the record from other controllers. Since I already have a huge headache, solving this problem, I highly appreciate any hint for a solution!!
Thanks in advance guys!
I finally found a solution to solve the issue. I still don't know, why the save code before did not work, but here is how I changed my code to make it work:
From my form, the data array comes in the following format:
Array
(
[EmployeesProject] => Array
(
[id] => 10
[user_id] => 0
[additional_information] => some comment text
[state] => absage
[Rejectreason] => Array
(
[0] => 1
[1] => 8
)
)
)
I searched for some solutions to save habtm relations in cakePHP directly with one call, but that does not seem to be possible in cake-1.3. So I created this pretty simple save routine in my EmployeesProjectController, which works perfectly fine for me:
if (!empty($this->data)) {
if ($this->EmployeesProject->save($this->data)) {
if(array_key_exists('Rejectreason', $this->data['EmployeesProject'])) {
foreach($this->data['EmployeesProject']['Rejectreason'] as $key => $rrId) {
$this->EmployeesProject->EmployeesProjectsRejectreason->create();
$this->EmployeesProject->EmployeesProjectsRejectreason->set('rejectreason_id', $rrId);
$this->EmployeesProject->EmployeesProjectsRejectreason->set('employees_project_id', $this->data['EmployeesProject']['id']);
if($this->EmployeesProject->EmployeesProjectsRejectreason->save()) {
}
}
}
}
}
Thanks #Yoggi for supporting me solving this issue!