Add multiple custom post type in URL - wordpress

I've made a WordPress website people can rent cars from brokers. So I have 2 custom post types:
broker
car
There are about 10 different cars on the website. All these cars are for each broker exactly the same.
So I've created a page template page-brokers.php that lists all the brokers.
If you click on a broker, you go to the detail page of the broker single-broker.php
The URL is saferental.be/broker/broker-name
On the detail page of the broker, you'll see all the 10 different cars. If you click on a car, you go to the car detail page, which is single-car.php
The URL is saferental.be/car/car-name
At the bottom of the car detail page, is a form to contact the broker you've selected previously. As you see in the URL, there is nothing mentioned of the selected broker.
When you select a broker, its information is saved in a session and pushed into hidden fields in the form.
Everything works perfectly:
- You select a broker
- You pick a car -> go to the car detail page
- Submit the car detail page form which sends the information to the selected broker.
The result I want to have:
- If you select a broker, you go to the detail page saferental.be/broker/broker-name/ (this is already okay)
- If you select a car, you go to the car detail page which should be something like this: saferental.be/broker/broker-name/car/car-name
Test website:
http://safelease.houston-1.hybridmedia.be/
The brokers are at the bottom of the homepage.

Do you want to keep that car detail page also will available under saferental.be/car/car-name?
If not, so — just put car detail page as child page to broker and url for this page will be saferental.be/broker/broker-name/car/car-name
If you want to many different urls for the same page — try to use this plugin https://wordpress.org/plugins/mapping-multiple-urls-redirect-same-page/

I have done something similar to this myself.
This would go in your functions file:
// Setup rewrite rules something like http://yourdomain.com/broker/my-broker/car/my-car
add_action( 'init', 'rewrites_init' );
function rewrites_init() {
add_rewrite_rule(
'broker/([-a-zA-Z0-9]+)/car/([-a-zA-Z0-9]+)$',
'index.php?broker=$matches[1]&car=$matches[2]',
'top' );
}
// Add variables
add_filter('query_vars', 'add_query_vars', 0);
function add_query_vars($vars) {
$vars[] = 'broker';
$vars[] = 'car';
return $vars;
}
// catch the request for this page
add_action('parse_request', 'parse_requests', 0);
function parse_requests() {
global $wp, $wp_query;
if(isset($wp->query_vars['broker']) && isset($wp->query_vars['car'])) {
// find the car post
$posts = new WP_Query( array(
'post_type' => 'car',
'name' => $wp->query_vars['car'],
'post_status' => 'publish'
));
if(!empty($posts) ) {
// set the global query or set your own variable
$wp_query = $posts;
// set the broker variable to use in your template
$broker = get_page_by_path( $wp->query_vars['broker'], OBJECT, 'broker' );
// include your custom post type template
if (include(locate_template('single-car.php', true))) {
exit();
}
} else {
// handle error
$wp_query->set_404();
status_header(404);
locate_template('404.php', true);
exit;
}
}
}
Then in your car template, you should be able to access the $broker post variable.
After you have setup your rewrite rules, you may need to go to Admin -> Settings -> Permalinks and save to set the rewrites.
Hopefully this helps you.

Related

What are the best practices to restrict access to my website except for two pages

I'm building a website on Wordpress with Bedrock/Timber/ACF.
This site is restricted for a big major part of it.
There is 4-5 pages available for anonymous users (home, contact, login & legal/privacy policy pages).
I'm currently managed it with Timber routing with like:
Routes::map('/blog/page/:num/', function ($params) {
if (!is_user_logged_in()) {
wp_redirect(home_url().'/login');
exit();
}
$user = wp_get_current_user();
if ($user->roles[0]=="subscriber") {
$post_type="publish";
} else {
$post_type="any";
}
$query = 'order=ASC&orderby=title&paged='.$params['num'].'&post_status='.$post_type;
Routes::load('list-blog.php', $params, $query, 200);
});
However I don't know if it's the good way to do it because I can't use wordpress template hierarchy, on the admin side everytimes I want to create a new page I have to create the road...
So my solution is not flexible and hard to maintain...
Do you have some advices?
EDIT:
I almost removed all my routes by using wordpress template hierarchy. But I still have routes link to the login page because I don't want to have mysite.com/wp/login but mysite.com/login.
I would use array_key_exists instead of checking for the first var because there a situation that the subscriber role is second.
If you are using ACF you could try to add a checkbox field to your pages and query for the variable of the field.
https://www.advancedcustomfields.com/resources/query-posts-custom-fields/
Routes::map('/blog/page/:num/', function ($params) {
if (!is_user_logged_in()) {
wp_redirect(home_url().'/login');
exit();
}
$user = wp_get_current_user();
$post_type = array_key_exists("subscriber", $user->roles) ? "publish" : "any";
$query = 'order=ASC&orderby=title&paged='.$params['num'].'&post_status='.$post_type;
Routes::load('list-blog.php', $params, $query, 200);
});

WP / Elementor Intercept Form and Redirect with Data

I have a form built in Elementor that I am looking to intercep, process the data and forward onto a third party then subsequently show the data on a "confirm" card.
I am able to build this whole process as a single page, setting each as display none with CSS then showing / hiding with JS as I receive AJAX responses. This isn't ideal as it breaks with JS turned off.
I haven't been able to find the right Elementor hook and way to populate a new page with PHP, has anyone had experience with this?
There are a few methods to POST data to another url from an Elementor web form.
One is using many of the API integrations such as Mailchimp, ActiveCampaign, Zapier etc. (see https://docs.elementor.com/article/281-form-faq) and (https://docs.elementor.com/category/405-integrations)
Another (very limited method) is by using the form's Action after Submit and choosing "redirect" and then using each field's short code as individual data strings in the url such as:
mysite.com/thank-you?fname=[field id="fname"]&lname=[field id="lname"]
You can even build your /thank-you/ page in Elementor to GET that data and populate Elementor elements such as text, title, links etc with the form field data. For example, I could drop a text element on the /thank-you/ page and choose dynamic instead of typing in the text area and from the dynamic drop down, choose "request parameter" and for the "type" choose GET and for the param name use your unique url keys such as fname, lname etc. You can even set prefix, suffix and even fallback text along with it.
Lastly, here is a write up on how to back end code passing data externally (https://developers.elementor.com/forms-api/#Form_New_Record_Action).
// A send custom WebHook
add_action( 'elementor_pro/forms/new_record', function( $record, $handler ) {
//make sure its our form
$form_name = $record->get_form_settings( 'form_name' );
// Replace MY_FORM_NAME with the name you gave your form
if ( 'MY_FORM_NAME' !== $form_name ) {
return;
}
$raw_fields = $record->get( 'fields' );
$fields = [];
foreach ( $raw_fields as $id => $field ) {
$fields[ $id ] = $field['value'];
}
// Replace HTTP://YOUR_WEBHOOK_URL with the actuall URL you want to post the form to
wp_remote_post( 'HTTP://YOUR_WEBHOOK_URL', [
'body' => $fields,
]);
}, 10, 2 );
And a thread with many more examples integrating with different APIs using that template as a primer (https://github.com/elementor/elementor/issues/2397)

Custom Post Type Action Hook / Transients

This question is in regards to a plug-in I'm developing.
I'm trying to fire a function each time a custom post type called "Product" is added or edited. In particular, I need a hook that fires before the meta boxes load on the add/edit page, but that only fires on that "Product" custom post type's edit page.
The function that will fire makes an API request, and caches the response in a transient.
The reason for the action hook is because in my current code, when the transient has expired, the add/edit page is broken during the first page load. However if you refresh the page after that, it shows up as intended. I'm fairly certain this is happening because the current conditional statement that checks the transient is located inside of the function that generates the meta box. So my theory is if I can set up an action hook to check the transient before the meta box is generated, it might solve the problem.
However I've got a second theory that the problem is being caused because of the time it takes to make the API request and return the response is longer than the time it takes for the page to load. So if there is an action hook that will delay page loading until the function finishes executing it would be an ideal solution, but I don't believe such an action hook exists. I'm not even certain if such a delay is possible.
I'd really appreciate any help or alternative suggestions you guys might have. Thanks for your time guys.
Code Example:
add_action( 'edit_product', 'llc_hook_campaign_find_active' );
function llc_hook_campaign_find_active() {
if (!$t_campaign_find_active){
limelight_cart_campaign_find_active();
return false;
}
}
Since you are using an action hook, it is not waiting for your API response.
Try using a filter hook instead.
Try using wp_insert_post_data
function filter_handler( $data , $postarr ) {
//make your API call, get the response and store it in post meta or data wherever you want
$response = 'your API response';
//e.g. update_post_meta($postarr['ID'], 'meta_key', $response); OR
//$data['post_content'] = $response;
return $data;
}
add_filter( 'wp_insert_post_data', 'filter_handler', '99', 2 );
In your case, following should work -
add_filter( 'wp_insert_post_data', 'llc_hook_campaign_find_active', '99', 2 );
function llc_hook_campaign_find_active( $data , $postarr ) {
if (!$t_campaign_find_active){
limelight_cart_campaign_find_active();
return $data;
}
}
I was able to make the API request before the meta boxes loaded on the Admin Add/Edit screen by using the action filter edit_form_top. That particular action hook is fired as soon as the Add/Edit page for any post/page/custom post type is loaded. In order to narrow it down so that the function only fires on the Add/Edit screen for my "product" custom post type, I used get_current_screen() along with an if statement.
add_action('edit_form_top', 'llc_hook_campaign_find_active');
function llc_hook_campaign_find_active() {
//Fetch current screen information
$screen = get_current_screen();
//Check if post type is "product"
if($screen->post_type == "product") {
//API Request that checks for an existing transient
$t_campaign_find_active = get_transient('campaign_find_active');
if (!$t_campaign_find_active){
limelight_cart_campaign_find_active();
return false;
}
}
}
Works like a charm.

How do you make a Wordpress plugin have "per post" options?

I'm building a Wordpress blog that requires a custom slideshow system with an admin panel on each post admin page (sort of like how Yoast's SEO plugin has options on each page).
How would I make the admin options appear on the post admin page?
Thanks,
Charlie
You are not providing much detail about your project but you probably want to make some meta_boxes and save the data to as custom fields.
Here is a truncated example culled from something I put together for a question at wordpress.stackexchange. The details of some of the functions are not important for your question here but it illustrates the general 'how'. Follow the link for working but sincerely beta code.
// author checkboxes
add_action( 'add_meta_boxes', 'assisting_editor' );
function assisting_editor() {
add_meta_box(
'assisting_editor', // id, used as the html id att
__( 'Editorial Tasks' ), // meta box title
'editor_tasks', // callback function, spits out the content
'post', // post type or page. This adds to posts only
'side', // context, where on the screen
'low' // priority, where should this go in the context
);
}
// this is the callback that creates the meta box content
function editor_tasks( $post ) {
// function details skipped; follow the link
}
// to save the data
add_action( 'save_post', 'save_metadata');
// callback for the save hook ^^
function save_metadata($postid) {
// function details skipped; follow the link
}

Silverstripe tumblr-like Post Types

I am trying to create a back-end interface for silverstripe that gives the CMS user the option to choose between a set of Post Types (like tumblr) in Silverstripe3. So they can choose to create a News Post, Video Post, Gallery Post, etc.
I initially started off giving all Posts the necessary fields for each Type and adding an enum field that allowed the user to choose the Post Type. I then used the forTemplate method to set the template dependent upon which Post Type was chosen.
class Post extends DataObject {
static $db = array(
'Title' => 'Varchar(255),
'Entry' => 'HTMLText',
'Type' => 'enum('Video, Photo, Gallery, Music')
);
static $many_many = array(
'Videos' => 'SiteVideo',
'Photos' => 'SitePhoto,
'Songs' => 'SiteMp3'
);
public function forTemplate() {
switch ($this->Type) {
case 'Video':
return $this->renderWith('VideoPost');
break;
case 'Photo':
return $this->renderWith('ImagePost');
break;
etc...
}
function getCMSFields($params=null) {
$fields = parent::getCMSFields($params);
...
$videosField = new GridField(
'Videos',
'Videos',
$this->Videos()->sort('SortOrder'),
$gridFieldConfig
);
$fields->addFieldToTab('Root.Videos', $photosField);
$photosField = new GridField(
'Photos',
'Photos',
$this->Photos()->sort('SortOrder'),
$gridFieldConfig
);
$fields->addFieldToTab('Root.Videos', $photosField);
return $fields;
}
}
I would rather the user be able to choose the Post Type in the backend and only the appropriate tabs show up. So if you choose Video, only the Video GridField tab would show up. If you choose Photo Type only the Photo's GridField would show.Then I would like to be able to call something like
public function PostList() {
Posts::get()
}
and be able to output all PostTypes sorted by date.
Does anyone know how this might be accomplished? Thanks.
Well the first part can be accomplished using javascript. Check out this tutorial and the docs let me know if you have questions on it.
The second part would be trickier but I think you could do something with the page controller. Include a method that outputs a different template based on the enum value but you would have to set links somewhere.
I managed this with DataObjectManager in 2.4.7 as I had numerous DataObjects and all were included in one page but I'm not sure if that is feasible in SS3.
return $this->renderWith(array('CustomTemplate'));
This line of code will output the page using a different template. You need to include it in a method and then call that method when the appropriate link is clicked.

Resources