add_action работает из functions.php потому что functions.php загружается движком и парсится на очень ранней стадии. То же самое делается в кастомных плагинах, которые тоже грузятся рано. А вот кастомные темплейты уже потом загружаются, и в них add_action не отрабатывается. Там можно ставить хук do_action(имя_экшна) для своих кастомных экшнов, а сам add_action надо тулить либо в functions, либо в плагин.
like-a-boss: Post-to-Posts пришлось использовать, это был наиболее гибкий вариант, так как у меня по сути некоторые custom post types выполняли роль таксономий. Например, есть cpt "событие", есть "таксономические" cpt - локация (у него есть место, адрес, фото, описание, карта и тд - таксономией не обойдешься), есть организация которая делает это событие (у нее тоже куча полей - название, описание, связки по географии, иерархические посты филиалов этой организации, контакты и связки с пользователями - руководители, манагеры и спортсмены, которые входят в эту организацию и тд). И таких связок было очень много. То же с cpt "объявления", которые разных типов и с кучей фильтров-таксономий, кроме аналогичных связок с тем же юзером (cpt user_profile, который в свою очередь тоже связан с кучей других объектов). Я рассматривал вариант добавлять эти поля к custom taxonomy именно через ACF Pro, пробовал даже создавать wp_termmeta и писать класс и template functions, аналогичные по работе с wp_postmeta/wp_usermeta, но в итоге вариант с P2P оказался более гибким и быстрым. Сам ACF Pro очень крутая штука, создание красивых и сложных бекендов для custom fields экономит тучу времени, например те же связки, галерея, repeater (повторяющиеся поля или наборы полей), flexible (добавление гибких наборов полей, по сути любых, в том числе repeater внутри flexible), карта, создание страниц опций и GUI для них, импорт-экспорт, синхронизация между multisite (а у нас эта платформа была еще и SaaS на базе WP Multisite), поддержка мультиязычки (там было 3 языка с возможностью расширения) и т.д. Так что использование этого плагина экономило очень много времени. А с недавних пор у ACF даже заработала более-менее функция создания этих форм и на фронтенде, что еще круче. Буквально 2 строчки - и форма готова. Правда, если это не просто создание поста/cpt, а нужно сохранять дополнительные данные - например, создать юзера, к нему cpt "user_profile", связать их, привязать к другим cpt через P2P - тогда понадобится дописывать немного, но ACF дает кучу разных экшнов на всех этапах, так что в паре с родными экшнами WP получается очень гибко, можно сделать практически все, что угодно. При этом девелоперская версия со всеми фичами и анлимом на количество сайтов стоит всего 100$ - это шара если учесть, сколько времени этот плагин экономит.
Да, я в принципе понял суть, у меня подобна кухня в прошлом году была, большой проект с кучей кастомного контента и сложных связей. При чем не только таксономии, но и postmeta через Advanced Custom Fields Pro, связи между custom post types через плагин Post-to-Posts, и это еще и на кастомный фронтенд завязано, куча своих форм с кучей chained селектов на базе Select2, который как раз и тягал все эти списки таксономий и прочих связок. Задачка еще та была) Например, на одной странице строилась таблица графика соревнований по разным видам танцев, в каждом элементе выводилось количество зарегистрированных участников, а также подробная инфа - откуда участники, их профессиональный класс, и так далее. Большая база пользователей с профайлам с кучей полей. Эта страничка к концу регистрации на соревнования генерила до 2-3к запросов в БД и собиралась больше 10 секунд. Пришлось разбить все на кусочки и отдельно их складывать в memcached с временем жизни от 5 до 15 минут, обновлять их тоже порциями, в результате чего страница грузилась как любая другая - не более секунды. В общем, масштаб вашей трагедии я себе представляю более-менее хорошо)
like-a-boss: да, это больное место запросов WP - SQL_CALC_FOUND_ROWS. Тут немного об этом, и погуглить можете - мне когда-то даже попадались решения. Функция get_posts как раз не использует это, из-за этого нет и пагинации. Эта функция для того и делалась - она идеально подходит, когда надо просто получить Х постов по нескольким параметрам. А для основных запросов (основа контента страницы) используется полноценный объект WP_Query, который корректно устанавливает все глобальные переменные, считает количество строк и тд. Пагинация - не единственный нюанс, там еще есть виджеты и прочие плюшки, которые могут быть завязаны на контекст основного запроса и глобальные переменные. Также с UNION / UNION DISTINCT кое-где есть проблемы, когда-то тут обсуждалось. В общем, с большими базами и сложными запросами всегда приходится маяться. Вам еще хорошо, что вы работаете с таблицей wp_posts, если залезете в сложные запросы на wp_postmeta, в которой значения ключей без индексов - вот там полная печаль с перебором всех строк. И там миллионы строк - обычное дело на более-менее большом сайте. Я, кстати, на всех крупных сайтах где нужен нормальный поиск или фильтрация по куче аргументов (в том числе postmeta) решил проблему на корню другим способом - поставил Elastic Search и все делаю через его индекс. Работает мгновенно, ищет очень хорошо - такой себе персональный гугл. Но тут уже без VPS не обойтись, на shared его не поставишь)
like-a-boss: этот плагин - это must-have) По get_posts vs WP_Query - вы имеете в виду все время, включая выполнение всех остальных операций, связанных с этими запросами, или чисто выполнение SQL? Функция get_posts это обертка, она парсит параметры и делает new WP_Query. Возможно, разница по времени как раз из-за количества аргументов, и, соответственно, сложности запроса.
500к - уже не крохотная бд, тут я немного недосмотрел. У вас это один сервер, или БД отдельно? Для таких объемов не мешало бы выделить отдельный БД сервер и оптимайзить его исключительно под базу. Да и смена движка с MySQL на Percona, а еще лучше - MariaDB, вполне может ускорить. Тяжелые запросы будут всегда, смотрите еще какие из них можно кешировать (механизмы transients, wp_cache_set) и выполнять эти сложные выборки только раз в Х минут / часов.
Тимофей Бережнов: "никак не дебажу" - о боги, как?))) Установите Query Monitor пока работаете и научитесь им пользоваться. Jquery, конечно, выход, но я бы делал server-side.
Андрей Хохлов: Вполне норм, но лично я не люблю всякий хлам в корне, у меня там только index.php, локально - чуть больше, но на продакшн это не идет. WordPress у меня в папке core (родная папка, только переименованная), wp-контент в папке content. А темы, плагины, переводы - все это уже внутри content, не вижу особого смысла выводить это отдельно. При таком подходе сама папка core - чистая установка wp у меня в репозитории стоит как git submodule, то же касается тем и плагинов, в первую очередь своих кастомных. Ну и тех, которые можно поставить субмодулем. Вообще эту концепцию взял у Mark Jaquith и Tom McFarlin.
Redmine - крутейшая штука, но вы правильно заметили - если сможете ее поставить. Я неплохо админю свои сервера, но с Ruby раньше никогда не сталкивался. Так вот я думал поседею, пока заставлю это все работать. Эти ваши юникорны и пессенджеры - это ад прям какой-то) В конечном итоге плюнул, развернул на отдельной коробочке digital ocean готовый образ с Redmine и за 5$ в месяц получил анлим CRM для своей небольшой компашки.
like-a-boss: А не пробовали через WP_Query, модифицируя через хуки? Условия запроса вроде бы достаточно простые, не вижу необходимости в кастомном запросе. Не гарантирую, что это сразу решит проблему, но в теории - вполне может. Попробовать не мешает.
Тимофей Бережнов: что не работает? какие ошибки и есть ли они? как дебажите?
и мое решение, и по ссылке на WPSE - оба надо допиливать под себя, они и не должны работать out of the box. Им нужно скормить правильные объекты, в пример использованы одни, но у вас могут быть другие. Как и где их взять - это уж сами смотрите. Если не знаете - уточняйте вопрос. Из текущего вопроса не совсем понятно, что за "пункт меню" вам нужен - какой его тип, нужно его title или slug и т.д.
Андрей: вот это уже ближе. Любую дату в тексте нужно провести через php-функцию strtotime( '23.04.2015' ), чтобы на выходе получить Unix Timestamp. Выше я об этом уже писал. А уже timestamp скармливаем функции human_time_diff() в качестве первого аргумента. Ну а как из всего текста поста выбрать именно этот фрагмент с датой - это уже другой вопрос. Если я правильно понимаю, вам надо, чтобы в тексте стояла не какая-то дата (события нарпимер), а время, прошедшее с того момента. В таком случае я бы посоветовал создать шорткод, в который бы вы передавали аргументом дату (в произвольном формате), а уже функция, обрабатывающая шорткод, пропускала бы эту дату через strtotime, потом human_time_diff и печатала текстом сколько времени прошло с той даты.
не верно. в бд как раз урл прописаны не относительные, а абсолютные, от wp_options - сам адрес сайта, без которого не поднимется ничего, до wp_posts, где ссылки в постах стоят.