What is the best way to check whether a request is an API request?
Note that the request might be a custom API request, which means it may be as follows:
mysite.com/wp-json/my_namespace/my_version/my_action
Obviously, I can check whether the API route wp-json, but there should be a built-in function to check that.
I need it to do some hooks such as
add_action('init', 'do_something_only_if_api_request');
function do_something_only_if_api_request()
{
if ( ! wp_api_request() ) {
return;
}
// do stuff
}
You can check defined('REST_REQUEST'). This constant is defined as true in rest_api_loaded() (and not defined otherwise).
There was a discussion of WP Rest API developers about the introduction of a new function like is_rest_request(). In the end they went for this constant.
As of December 2016, the REST API documentation is pretty poor regarding everything which doesn't look like an endpoint.
However, a few functions exist and you can find the documentation right in the file as they're very well documented, see: wordpress/wp-includes/rest-api.php
If you want add an action only on an REST API call then you probably want to hook the action: rest_api_init, it would look like:
add_action('rest_api_init', 'do_something_only_if_api_request');
function do_something_only_if_api_request($wp_rest_server)
{
// do stuff
}
You can find the details in the PHPdoc comment:
/**
* Fires when preparing to serve an API request.
*
* Endpoint objects should be created and register their hooks on this action rather
* than another action to ensure they're only loaded when needed.
*
* #since 4.4.0
*
* #param WP_REST_Server $wp_rest_server Server object.
*/
In my case, the plugin is doing wp_redirect for users to login page.
I want to avoid this in case that the rest api is called.
So I'm using
if (! strpos( $_SERVER['REQUEST_URI'], 'wp-json')) // It's not a rest-api call
Related
I use API platform with Symfony5, and I created a service to verify a statement if it is correct.
This statement if is correct, I want to change an existing object instead of adding on a POST request.
So, on POST request I created an event with PRE_WRITE events priorities, and this event calls a service who verifies if the statement is correct if is true, I edit an existing object.
All that work correctly without any problem, but the POST request is always for adding a new object, so, I get a new line on the database table.
Is there any solution, to return 200 responses on the edit object?
Try adding a Response to the kernel.view event like:
....
public function yourEvent(Event $event)
{
// do your service stuff
// ...
$event->setResponse(new Response('ok', 200));
}
Maybe you need to use PUT - for changes.
POST- to create a record
I'm trying to implement in my symfony project some api. Currently the project have many controller with standard crud, based on html table, form/validator etc.
I'm looking to the api-platform project that seam to make very easy the construction of standard rest api, and for the GET part it fit my necessities.
But for the POST/PUT/DELETE part it seam a very basic persist action on an entity, and suddenly in my project, i need to do many more actions after the persist of the entity.
I've red the docs and I'm really confused on how to do that...
I see two possibilities:
Using the event system, subscribing for the POST_WRITE for every entities
Creating a custom action for every create/update/delete actions of an entity
In both the case, I would have a really high number of single actions or event subscriber in the project (30/40), and it's really unconfortable to maintain. Also I probably have to replicate the same code that I already have in the controller, to maintain the old form system until is all rewitten in an API format.
Any suggestion on how to approach this problem?
There isn't a way to use the same controller actions, like in the FOSRestBundle, so that I can receive the data, do the various validation/persist/extra actions, and then return a result that is managed by the api-platform events?
Any way to manually call some part of the api-platform, like the deserialization/serialization, the filter and pagination from a standard controller action?
Thanks to all
Cheers
Daniele
Forgive me if I do not completely understand your question but if you already have the functionality written in the controller and you want to access the same actions via an api then maybe you can set multiple routes on each action and depending on how the action was called you can respond differently. For example:
/**
* #Route("/api/v1/tester", name="api_tester")
* #Route("/tester", name="tester")
*/
public function testerAction( Request $request )
{
$route = $request->attributes->get('_route');
if( $route == "api_tester" )
#..do things the api way
response = array( "success" => 1, "data" => $return_string );
return new Response( json_encode( $response ) );
} else { //non-api
$this->render('tester/basic.html.twig', array();
}
}
You can evaluate which route was used and in various sections of your actions you can handle things differently based on if the action was called via the api or from a normal request.
A project I am working on contains a search feature. I would like users to have the ability to bookmark a page along with their query so they can bookmark their search page.
In the traditional implementation, I simply used GET parameters in the URL.
However, with symfony I am having a hard time understanding what is best to use for this functionality? is it just a matter of preference?
Should I just create a controller such as:
search/{query}/{page}
and when a user clicks on an option
description/{id}
or should I implement the traditional get variable in the URL:
Note the URL's on Stack Overflow, for example. When you search for something and you navigate the pages, the URL is something like:
search?q="nav&page=2 But then when you click on a post the URL becomes something like questions/19157969/expanded-navigation-by-default, so it is kind of a mix of the two.
Couldn't SO use something like search/search-term-here/2 for the same functionality? is there any benefit for picking GET over the alternative, using Symfony?
I appreciate any suggestions! many thanks in advance!
It is a matter of preference. Some say that the URL is more important than the query string for SEO purposes. Also, /search/{query}/{page} is better looking in general, but that's just my opinion.
Search terms can get very long. Perhaps your searching mechanism is fairly simple, but personally I wouldn't want to make that assumption, especially if you plan on using the same feature for something in future you haven't designed the scale of yet.
Using a typical GET string is safe enough in my opinion, the controller should know what variables it needs, and GET queries generally don't (shouldn't) have an impact on routing.
However if you want to achieve pretty search URLs which can always be revisited, you could try storing each search in a database.
An example (untested):
class SearchController extends Controller
{
/**
* #Route("/search" name="search")
* #Method("GET")
*/
public function searchAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$search = new Search();
$search->setQuery($request->query);
// The query field would be of an object type in this example,
// but you could store the data any way you want really.
// Add any additional information about the search/request to the entity.
$em->persist($search);
$em->flush();
$this->redirect($this->generateUrl('search_results', array(
'search_id' => $search->getId()
)));
}
/**
* #Route("/search/{search_id}", name="search_results")
* #Method("GET")
* #Template()
*/
public function resultsAction($search_id)
{
$em = $this->getDoctrine()->getManager();
if(! $search = $em->getRepository('AcmePostBundle:Search')) {
throw $this->createNotFoundException();
}
$query = $search->getQuery(); // This will be a ParameterBag
// Run your search filters
return array(
// Your search results
);
}
}
In this example the request is to an action with a GET string as normal (could be POST if you prefer), which saves the search data and then redirects to an action which gets the search data from the database, does its filtering and show the results.
I can't see any real performance loss in a technique like this, the database queries to insert and select the search should be relatively small, but I can see the table in which you store them getting very full very quickly.
Then again it is always useful (occasionally) to draw reports on popular search terms.
I have organic groups setup and within those group users are allowed to post certain content.
What I woulkd like to do is, when you create a node inside an organic group, it automatically defaults back to frontpage of the group, or the same page that I used to create the node.
At present it defaults to the node view page.I assume there must be a way to add some kind of code so that after the node creation it defauls back to its origin. I.E. the page from where the node was created from.
thanks :)
UPDATE: Got the below, but not entirly sure how to ensure that it redirects back to the GROUP node, from where it was created,
<?php
/**
* Grabs current node ID
*/
$node_nid = nid;
/**
* Implements hook_form_alter().
*/
function mod_form_alter(&$form, $form_state) {
$form['buttons']['submit']['#submit'][] = 'mod_form_finish_redirect';
unset($form['buttons']['preview']);
}
/**
* Custom submit handler. Overwrites the form redirection variable.
*/
function mod_form_finish_redirect($form, &$form_state) {
$form_state['redirect'] = '/content/<?php print $node_nid; ?>';
}
?>
I would recommend the rules module. Rules is a great module that allows you to do many kinds of workflow and it is perfect for this. You can write a rule that triggers when a specified node type is created (and include any other conditions you require as well). After the node is created you can specify a redirect action rule to the home page. This can all be done without any code.
Rules (as 'We Love Drupal' says) is a possibility, but also quite a big module for such a small change in behavior. Another option is to write a custom module implementing hook_form_alter setting the #redirect value of the form.
Keep in mind that of seeing the node you have just created is important feedback for a user. When you perform an action, you want confirmation that you have achieved your task. While it's technically possible to do what you ask, it may be bad for usability.
I had the same requirment. Rules worked for me.
Whenever a content item is created, a message is displayed like this:
[Content Type] [Name] has been created.
Is there any way to disable this message for specific users? Or for all users would be fine too.
I think the best practice would be to use hook_nodeapi() and drupal_get_messages('status'). The $op for hook_nodeapi() would be insert. Ex:
mymodule_nodeapi(&$node, $op) {
if ($node->type == 'content_type_to_check_for' && $op == 'insert') {
drupal_get_messages('status');
}
}
It's node_form_submit that is creating those messages. You could pretty easily use hook_form_alter on the node form and use your own version of node_form_submit instead. All you would need to do, would be to copy the function and add an user_access('whatever') check before that message is created.
Alternatively, you could in preprocess_page function, check which messages is being served, and remove unwanted ones, but that would be a bit more tricky. Should be possible with some regex. On the other hand, this method would be a bit more upgrade friendly, since you could remain using the node_form_submit function and would get future changes if any.
Best way would be to user Disable Messages module.
There are many kind of messages that can be disabled by this module:
Filter out messages that match a full text string exactly.
Filter out messages that match a regular expression.
Permissions to specifically hide all messages of a given type from any role.
Disable all filtering for specific users.
Disable all filtering for specific paths.
Apply filtering only for specific paths.
Debug system to get messages in the HTML without showing it to the end users.
Here is the way I discovered to hide such messages for specific content types (the node type is 'request'):
// specific node type form alteration hook (implements [hook_form_FORM_ID_alter][1]())
function MYCUSTOMMODULE_form_request_node_form_alter(&$form, &$form_state) {
// ...
// custom validation function
$form['#validate'][] = '_custom_request_node_form_validate';
// ...
}
function _custom_request_node_form_validate($form, &$form_state) {
//...
// here we can set a submit handler that is executed before
// node_form_submit which sets the messages we are trying to hide
$form_state['submit_handlers'][] = '_custom_request_node_disable_msg';
//...
}
function _custom_request_node_disable_msg($form, &$form_state) {
//...
// clear status messages
drupal_get_messages('status');
}
If you want to use the Rules module, then you can use the new module I created called "Better Rules Message".
By using this you can setup a rule that will delete all of the messages after a node is being created...
Hopefully this will be added to the main Rules module in the near future.
googletorp is right (about the submit function). But unfortunately you can't decouple the message from the node submit function and duplicating the functionality (without the message) is going to mean your site might break when a security release is issued. You'd have to maintain your own version of that function. It's probably not a big deal but it's a good idea to follow best practice.
You'll need to write your own submit hook either before or after node_form_submit gets called.
With a submit hook after the node save, you could remove the message from $_SESSION['messages'] if the messages array was easy enough to work with. I imagine that would be simple enough. See drupal_set_message
OR
You could write some class in CSS in your body tag and set the display to none when status messages are returned on the page that the node form submits to. But that might put your business logic in your theme layer which should be avoided.
You can use stringoverrides module in drupal ! :)
You could try using the following module to disable specific messages in Drupal - http://drupal.org/project/disable_messages