WordPress как на ладони

Как создать новые колонки таблицы в базе данных? Или использовать массив в метаданных поста

Создаю плагин WordPress, который после активации должен к стандартной таблице wp_posts добавить две колонки positive_users_reaction и negative_users_reaction. В ячейках этих колонок хочу складывать id пользователей которые положительно/негативно оценили каждый пост. Вот код.

function create_rows()
{
	global $wpdb;
	require_once ABSPATH . 'wp-admin/includes/upgrade.php';

	$table_name = $wpdb->get_blog_prefix() . 'posts';
	$charset_collate = $wpdb->get_charset_collate();
	$sql = "ALTER TABLE {$table_name}
	(ADD COLUMN
	 positive_users_reaction TEXT NOT NULL AFTER comment_count,
	 negative_users_reaction TEXT NOT NULL AFTER positive_users_reaction);
	{$charset_collate}";
	dbDelta($sql);

	$wpdb->insert(
		$table_name,
			array(
				'positive_users_reaction' => [],
				'negative_users_reaction' => [],
			)
		);
}

create_rows();

Подскажите, почему оно ничего не создает и не назначает скобки объекта по умолчанию (Ведь это же правильный стартовый шаблон если надо получать array? Как это поправить что б создавало две колонки?

Заметки к вопросу:
Kama 1.3 года назад

dbDelta() не работает с ALTER TABLE. Используй $wpdb->query(). Ну и как в комментах ниже написали, лучше метаполя юзать. Не надо родные таблицы ВП использовать для этого, кроме случаев когда вы 100% знаете и понимаете зачем вам именно так нужно.

0
BlackStar
1.3 года назад 6
  • 1
    el-lable620 el-lable.ru

    Вместо {$table_name} должно быть ${table_name}.
    Со второй переменной аналогично.

    BlackStar 1.3 года назад

    Это не помогло. Да и в документациях оно так пишется

    el-lable 1.3 года назад

    Вам надо делать без затрагивания таблицы, на самом дела это странный подход.
    Вместо колонок, вам надо смотреть в сторону метаполей.
    https://wp-kama.ru/function/update_post_meta

    Kama 1.3 года назад

    Вместо {$table_name} должно быть ${table_name}.

    Это PHP, не JS smile

    el-lable 1.3 года назад

    Оказывается можно и так и так...

    Kama 1.3 года назад

    Не помню чтобы видел, что где-то в PHP так писали ${table_name}.

    el-lable 1.3 года назад

    Странно, но я наоборот запомнил именно такой вид написания как в шаблонизации JS.
    Мало того, оно даже в редакторах правильно подсвчивается.

    BlackStar 1.3 года назад

    Спасибо за совет. Так

     static function activation()
    	{
    
    		function create_rows()
    		{
    			global $wpdb;
    			require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    
    			$table_name = $wpdb->get_blog_prefix() . 'posts';
    			$charset_collate = $wpdb->get_charset_collate();
    
    			$wpdb->query("ALTER TABLE ${table_name} ADD COLUMN positive_users_reaction TEXT NOT NULL AFTER comment_count");
    			$wpdb->query("ALTER TABLE ${table_name} ADD COLUMN negative_users_reaction TEXT NOT NULL AFTER positive_users_reaction");
    		}
    		create_rows();
    
    		flush_rewrite_rules();
    	}

    Код работает. Можете ещё помочь с второй частью вопроса - Хочу что б изначально в ячейках колонок хранился как-бы шаблон, который указывал что это массивы из значений. Но вот такой код

    function create_rows()
    		{
    			global $wpdb;
    			require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    
    			$table_name = $wpdb->get_blog_prefix() . 'posts';
    			$charset_collate = $wpdb->get_charset_collate();
    
    			$wpdb->query("ALTER TABLE ${table_name} ADD COLUMN positive_users_reaction TEXT NOT NULL AFTER comment_count");
    			$wpdb->query("ALTER TABLE ${table_name} ADD COLUMN negative_users_reaction TEXT NOT NULL AFTER positive_users_reaction");
    
    			$wpdb->insert(
    				$table_name,
    				array(
    					'positive_users_reaction' => '[]',
    					'negative_users_reaction' => '[]',
    				)
    			);
    
    		}
    		create_rows();

    Не создает некое представление для ячеек. Не могу найти пример где б в mySQL говорилось про хранение массивов в ячейках таблицы базы.

    Kama 1.3 года назад

    В mySQL нет массивов - есть строки, который могут содержать JSON. Чтобы по умолчанию они там хранились надо в описании колонки указать DEFAULT, как-то так.

    ALTER TABLE ${table_name} ADD COLUMN positive_users_reaction TEXT NOT NULL DEFAULT '[]' AFTER comment_count

    Далее в коде всегда обрабатываешь эти значения строк через json_encode() (для записи значения) json_decode() (для получения).

    $wpdb->insert() тут вообще не подходит!

    ПС: Ну и как писалось выше и учитывая что у вас такие вопросы. Все же лучше хранить это в метаполях...

    BlackStar 1.3 года назад

    Спасибо, большое. Не очень понимаю, как в метаполях держать массивы и ещё и работать с ними. С документации понял, что там работают с обычными переменными. Ещё, был бы благодарен если бы объяснили почему так сильно все рекомендуют не трогать стандартные таблицы WP? Кроме вопросов безопасности там есть какие-то опасности?

    Kama 1.3 года назад

    Потому что в этих таблицах храняться данные меню, других плагинов, типов записей, которые могут не оносится к вашей users_reaction логике. Потому что у этих таблиц есть задокументировання структура, с которой работают и другие плагини и черт знает кто и как может их использовать. Добавление новых полей туда может где-то чтото сломать или может быть какой-то конфликт который сломает работат вашего кода или кода другого плагина. В будущем ВП теоретически может запретить добавлять в эту таблицу свои колонки. Вообще может быть что угодно.

    С другой стороны, для любых дополнительных данных записей в WP предусмотрены метаполя, которые также задокументированы и имеют своюлогику, кэшируются и т.д. По сути у вас нет ни одной причины хранить данные массива в основной таблице, а не в таблице метаполей. Вам в любом случае придется с ними работать и писать какой-то кастом под них, и делать это все равно где - в метаполях или для основной таблицы. Так зачем совать данные не туда где для них специально приготовлено место?

    Чтобы менять привычную структуру движка, вам нужна веская причина, какая она у вас? просто так хочется? Это не причина - это просто ваше желание не подкрепленное никакой логикой.

    Не очень понимаю, как в метаполях держать массивы и ещё и работать с ними.

    В БД вообще нельзя хранить массивы. Там есть только строки и массивы храняться там в виде сериализоыванной, JSON, какой-то вашей кастомной строки, которая при получении парсится и превращается в массив.

    Прочитай как работают функции update_post_meta() и get_post_meta(). В значения можно указывать массив и он будет храниться в серриализованном виде в БД. Как вариант можно вручную сохранять туда JSON и работать с ним.

    Тут главный вопрос как вообще тебе придется работать с данными, просто сохранять их и потом считывать где нужно - 100% метаполя. Если нужна будет сортировка или выборка по отдельным полям из этого массива, то эти поля нужно сохранять как отдельные метаполя и потом делать сотировку по отедльным метаполям.

    BlackStar 1.3 года назад

    Спасибо, за объяснения. С update_post_meta() как-то понятнее не стало. Это получается Надо будет каждый раз получать данные $str=get_post_meta(), потом превращать их в массив str_split($str) добавлять/отнимать значения из этого массива, а потом снова превращать в строку и возвращать в это метополе? Или есть какое-то решение попроще ?

    function addRatingForPost(){
    
    	$postId = $_POST['postId']; // ID поста
    	$currentUserId = $_POST['currentUserId']; // ID пользователя которому понравился пост
    	$prevData = get_post_custom_values( 'positive_users_reaction', $postId, true );
    
    	if( empty( $prevData ) ) {
    		update_post_meta($postId, 'positive_users_reaction', '[]');
    	}
    
    	$prev =  str_split($prevData);
    	$newData = array_push($prev, $currentUserId);
    	$n2 = implode(",", $newData);
    
    	update_post_meta($postId, 'positive_users_reaction', $n2, $prevData);
    
    	wp_die();
    }

    Этот код чего-то не отрабатывает. Отдает 500

    el-lable 1.3 года назад
    function addRatingForPost() {
    	if( empty( $_POST['postId'] ) || !isset( $_POST['currentUserId'] ) ) wp_send_json( ['error' => __( 'Wrong Data' )] );
    
    	$postId = (int)$_POST['postId']; // ID поста
    	$currentUserId = (int)$_POST['currentUserId']; // ID пользователя которому понравился пост
    
    	$data = !empty( get_post_meta( 'positive_users_reaction', $postId, true ) ) ? get_post_meta( 'positive_users_reaction', $postId, true ) : [];
    	if( !in_array( $currentUserId, $data ) ) $data[] = $currentUserId;
    	update_post_meta( $postId, 'positive_users_reaction', $data );
    
    	wp_die();
    }

    Как то так, и в PHP не принято писать верблюжьей нотацией.

    BlackStar 1.3 года назад

    Спасибо, буду знать. Как-то не так скрипт обновляет.

    При нажатии кнопки новым пользователем идет перезапись параметров, а необходимо дополнение массива данных. Тут явно должно задействоваться значение $prev_value из update_post_meta. Не очень понимаю, как он работает ваша строка

    if (!in_array($currentUserId, $data)) $data[] = $currentUserId;

    думал, что в meta_value будут храниться значения по типу {1,5,2}

    Kama 1.3 года назад

    Не понимаю, что тут сложного-то? Ну, потыкай чуть посмотри как работает, я вообще уже молчу что в описании функций есть примеры все которые тебе могут пригодится.

    Есть две функции get_post_meta() и update_post_meta(). Одна получает, другая сохраняет что ей дали. Все!

    Остальное - твоя логика - как напишешь так и будет работать. Нужно дополнять данные? Так получи то что есть сперва, добавь туда что надо и сохрани что получилось.

    $data = get_post_meta( $post_id, 'meta_key', true );
    if( ! $data ){
    	$data = [];
    }
    
    $data[] = 'new el';
    
    update_post_meta( $post_id, 'meta_key', $data );

    Обе функции понимаю если в значении массив (он сериализуется/десереализуется при сохранении/получении). Если нужно не сериализовать, а хранить как JSON, например, то пиши конвертацию туда и обратно отдельно (из коробки эти функции сереализуют массивы, а не в JSON строку первращают их).

    BlackStar 1.3 года назад

    Такое впечатление, что не понимаете задачу. Ваш пример не чувствительн к тому, что уже было записано в ячейке (То что я ищу)... Аналогичен тому, что написал el-lable

    if (!in_array($currentUserId, $data)) $data[] = $currentUserId;
    	update_post_meta($postId, 'positive_users_reaction', $data);

    При вызове вы каждый раз перезаписываете новыми данными, не обращая внимания на то, что там хранилось... Параметр $prev_value из документации не фигурирует. Пример. Вот в 'meta_key' ячейке хранилось значение (ID пользователя поставившего лайк), дальше мы вешаем срабатывания приведенного скрипта на какую-то кнопку. И в вашем случаи в ячейке базы будет храниться значение ID последнего пользователя поставившего лайк. А не всех
    PS там что-то с приведением типов не так.

    <?php 
    $data = [];
    $data[] = 'new el';
    echo $data;

    это так не сработает.

    el-lable 1.3 года назад

    если вам нужно знать кто последний совершил некое действо(лайк или дизлайк)
    поменяйте эту строку в моем примере с

    if( !in_array( $currentUserId, $data ) ) $data[] = $currentUserId;

    на

    if( !in_array( $currentUserId, $data ) ) {
    	$key = array_search( $currentUserId, $data );
    	unset( $data[$key] );
    }
    $data[] = $currentUserId;

    При таком варианте $currentUserId у вас будет всегда последним в масиве, и это всегда позволит получать вам последнего пользователя, если он был конечно, из него(массива)

    /* $last_active_user_ID = !empty( $data ) ? $data[count( $data ) - 1] : false; */
    $last_active_user_ID = end( $data );
    BlackStar 1.3 года назад

    нет, это не то что необходимо. el-lable скрипт который вы описали работает так -> заходит пользователь (id=1) на страницу читает пост, жмет кнопку Понравилось в базу в ячейку пишется вот такое a:1:{i:0;i:1;} Приходит следующий пользователь (id=2) на ту же страницу, читает пост, нажимает кнопку Понравилось теперь в ячейке базы находиться a:1:{i:0;i:2;} так оно отображает... Так понятно ? А по хорошему после второго пользователя там должно было быть [1,2] (ну или ассоциативный массив, не важно) после третьего [1,2,3] где цифры - id пользователей которые нажали кнопку Понравиться.

    el-lable 1.3 года назад

    Какая вам хрен разница что по факту хранится в ячейке таблицы БД?
    Вы получаете данные get_post_meta() - в виде массива, и записываете их с помощью update_post_meta() тоже массивом, ничего с данными при передачи-получении конвертировать или как-то преобразовывать не нужно, эти матоды это делают сами.
    И в каком формате хранятся данные в ячейке таблицы, сериализованные или в виде json, да хоть в XML-е - на кой ляд вам это нужно?

    Kama 1.3 года назад

    Ваш пример не чувствительн к тому, что уже было записано в ячейке (То что я ищу)...

    Конечно чувствителен, еще раз тот же пример с комментариями в которых видно где и как он чувствителен:

    // Получаем что сейчас хранится в БД, в метаполе 'meta_key'
    $data = get_post_meta( $post_id, 'meta_key', true );
    
    // Если сейчас хранится ничего (поля нет или там пустая строка),
    // то укажем что данные - это пустой массив.
    if( ! $data ){
    	$data = [];
    }
    
    // ДОБАВЛЯЕМ нового пользователя в массив!
    // ВНИМАНИЕ не перезаписываем что там было до этого, а добавляем к тому что было!
    $data[] = get_current_user_id();
    
    // Сохраняем весь массив (с тем что там было и что мы добавили)
    update_post_meta( $post_id, 'meta_key', $data );
    BlackStar 1.2 года назад

    Спасибо за объяснение строчек кода. Вопросы всё же остались. После первого захода, в ячейку массива записало
    a:1:{i:0;i:1;} после второго пользователя с другим ID в ячейке уже было - a:2:{i:0;i:1;i:1;i:4;} после третьего пользователя a:3:{i:0;i:1;i:1;i:4;i:2;i:3;} Может чего не понимаю, но это не очень похоже на стандартные объект php... ключ-значения по разным элементам. Это такая особенность WordPress? Не понимаю как это отфильтровывать. Допустим, что б один и тот же пользователь не имел возможность добавить ID в массив больше одного раза.

    BlackStar 1.2 года назад

    Ага, всё дошло сериализованый массив. Уникальные значения через $data = array_unique( $data ); Ещё раз всем спасибо.

    Комментировать
На вопросы могут отвечать только зарегистрированные пользователи. Вход . Регистрация