I successfully created a custom admin module and loaded successfully in the menu and rendered phtml file successfully.
Then i tried to load some dummy data via ajax using angularJS in same admin module when clicking a button, but the ajax call returns the html content of dashboard of magento admin.
I followed this link
Following is the code for controller
<?php
namespace DVendor\DemoModule\Controller\Adminhtml\DemoPart;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\Controller\Result\JsonFactory;
class Index extends \Magento\Backend\App\Action
{
/**
* #var \Magento\Framework\View\Result\PageFactory
*/
protected $resultPageFactory;
protected $jsonPageFactory;
public function __construct(Context $context,PageFactory $resultPageFactory,JsonFactory $jsonPageFactory){
$this->resultPageFactory = $resultPageFactory;
$this->jsonPageFactory = $jsonPageFactory;
parent::__construct($context);
}
public function execute()
{
if($this->getRequest()->isAjax()){
$result = $this->jsonPageFactory->create();
$test=array(
'Firstname' => 'What is your firstname',
'Email' => 'What is your emailId',
'Lastname' => 'What is your lastname',
'Country' => 'Your Country'
);
return $result->setData($test);
}
else{
return $resultPage = $this->resultPageFactory->create();
}
}
}
?>
Following is the JS Code used in phtml file
$http({
method:'POST',
url:'<?php echo $this->getUrl('demomodule/demopart'); ?>',
//data:$.param({'limit':$scope.definedlimits.value}),
headers:{'Content-Type':'application/x-www-form-urlencoded'}
}).then(function(response) {
var data = response.data;
console.log(data);
},function(response){
console.log(response.data);
});
Related
List with Views published nodes in the form of a table. Add to a table with a column that will contain a link. By clicking on the link ajax request should be sent, which makes the node unpublished.
I have views, have a link, have alert-ajax. But I don't understand how I can change the status from 0 to 1 programmatically.
My code in controller -
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\RemoveCommand;
use Drupal\Core\Controller\ControllerBase;
class CustomAjaxLinkController extends ControllerBase{
public function customAjaxLinkAlert($node) {
$query = \Drupal::entityQuery('node');
$query->condition('status', 1);
$node = $query->execute();
$status = $node-> isPublished();
if ($status === TRUE) {
$node->status = 0;
$node->save();
}
$response = new AjaxResponse();
$selector = '.customAjaxLinkAlert' ;
$response->addCommand(new RemoveCommand($selector, $node));
return $response;
}
}
There is an error in it, but I cannot figure out what I am doing wrong
You can past data's via ajax in a Drupal Controller
$.ajax({
type: 'POST',
url: '/your_module/your_function',
data: {'data': data},
});
Then in your Controller
<?php
namespace Drupal\your_module\your_contoller;
use Drupal\Core\Controller\ControllerBase;
/**
* Class your_contoller.
*/
class your_contoller extends ControllerBase {
public function your_function() {
do what you want
}
}
OR
Your button is inside a form and will submit the value.
I am using the Symfony form component. I have many forms in my project.
To perfectly learn the form component was a long way to go, but now I love it. I love also the automatic validation and so on.
So. now I want to learn and use React.js in my project.
But it seems, there is no way I can use the validation and form builder like before for the projects? Am I right there?
While, in an API context, you won't use the Form Component to actually render your form in HTML format ($form->createView() method), you can still benefit from all the magic it offers: validation, form events, etc. API or not, I personnally think you should always use Form Types in Controller mutations.
For example, using FOSRestBundle, consider some simple Controller action looking like this:
/**
* #Rest\Put("posts/edit/{id}", name="posts.edit", requirements={"id"="\d+"})
*
* #param Post $post
* #param Request $request
*
* #return Post
*
* #throws BadRequestException
*
*/
public function edit(Post $post, Request $request): Post
{
$form = $this->createForm(PostType::class, $user);
$form->handleRequest($request);
$form->submit($request->request->all());
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
return $post;
}
// Any error handling of your taste.
// You could consider expliciting form errors.
throw new BadRequestException();
}
Note that Post entity and PostType form must be created, of course. I won't be detailing it here as there is nothing special to say about them in this context.
Also note that we don't check if the form is submitted. Rendering the form as HTML being React's job in your case, this action won't be used to GET anything, it is exclusively a PUT route. This means that any Request coming there MUST be a PUT Request containing the correct data to be handled by our PostType, submitted in your case by an HTML form manually built in React.
Furthermore, slightly out of the question's scope, FOSRestBundle subscribes to whatever your action returns and automatically serializes it to the configured format (let's say JSON in your case, I guess). This means that our example of action can return two possible responses:
A Response with status code 200 containing our serialized Post. (You could also consider a 204 and return nothing.)
A Response with status code 400 containing whatever we want (let's say form errors).
Allow me to lead you to the FOSRestBundle's documentation.
You can use your form created with the formBuilder without problem.
You must get your form with axios and create a new component like this:
const url = 'localhost/post/new';
const ref = useRef(null);
const [form, setForm] = useState('');
const fetchData = () => {
axios.get(url))
.then(function (response){
setForm(response.data);
})
.catch(function (error){
//something
})
;
}
const MyComponent = () => {
useEffect(() => {
const element = ref.current.firstChild;
if (ref.current.firstChild === null)
return;
element.addEventListener('submit', () => {handleSave(event)});
return () => {
element.removeEventListener('submit', () => {handleSave(event)});
};
}, []);
return (
<div ref={ref} dangerouslySetInnerHTML={{ __html: form }} />
);
};
const handleSave = (event) => {
event.preventDefault();
let formData = new FormData(event.target)
let action = event.target.action;
let files = event.target.querySelectorAll('[type="file"]');
if (files)
files.forEach((file) => {
formData.set(file.name, file.files[0])
});
axios.post(action, formData)
.then(function (response) {
Swal.fire({
icon: 'success',
title: response.data.message,
showConfirmButton: false,
timer: 1500
})
//Do something else
})
.catch(function (error) {
error.response.status === 422 ?
setForm(error.response.data)
:
console.log(error);
});
}
return (<MyComponent/>);
So, now you can get the form with html components and render it with the React Component.
If you get some validation error you get a 422 status and you can replace the form with setForm().
In your Symfony Controller you must set something like this:
#[Route('/post/{state}/{id}', name: 'post', defaults: ['state' => null, 'id' => null])]
public function post(
?string $state,
?int $id,
Request $request,
EntityManagerInterface $em
): JsonResponse|Response
{
if ($state == 'new') {
$post = new Post();
$form = $this->createFormBuilder()
->add('title', TextType::class)
->add('content', TextareaType::class);
$form = $form->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
$em->persist($post);
$em->flush();
return $this->json(['message' => 'post added!'], 200);
}
return $this->renderForm('{{ form(form) }}', [
'form' => $form
]);
}
}
I have reduced the function only for the form, but you can use it for all your requests.
Probably not because your form is intended for server use: validation/show errors/data normalization/sanatization/etc.
Usually you use React to display HTML and connect it to your server using an API.
I'm building a REST-full web-app using Laravel 5.2 (for the back-end) and AngularJS for the font-end. I connect to the Laravel back-end by the use of an API. Now I have stumbled upon the following problem: I can properly upload an image, but deleting it again is not working.
The files are uploaded into the Larvel public/images/uploaded/ folder.
This is my Angular Service (the http request fired when clicking the 'delete' button) where the variable imageToDelete is the relative path to the image.. So Far so good, the request is firing and the imageToDelete variable is populated.
function deleteProfileImage(imageToDelete) {
return $http({
method: 'DELETE',
url: '/api/pictures/' + imageToDelete
})
.then(deleteProfileImageSuccess)
.catch(deleteProfileImageError);
function deleteProfileImageSuccess(response) {
$log.info('Deleting profile picture Success.');
console.log(response);
return response;
}
function deleteProfileImageError(error) {
$log.info('Deleting profile picture failed because: ' + error.data);
return error;
}
}
This angular HTTP request fires a DELETE request to the following function in my Laravel Controller.
public function destroy($imageToDelete)
{
if(Storage::delete($imageToDelete)) {
return response()->json(['success' => 'success', 'message' => 'File Deleted']);
} else {
return response()->json(['error' => 'Deleting Image failed.'])
->setStatusCode(Response::HTTP_BAD_REQUEST);
}
}
And here, the Storage::delete($imageToDelete) does nothing. It does not delete the file provided with the Angular DELETE request.
Some things I have already tried:
Working with File::delete() instead of Storage::delete()
Working with unlink() instead op the Laravel Facades;
Sending the imageToDelete as data with the HTTP DELETE request (so not in the URL).
But all without success.
How can I make Laravel (PHP) delete the image?
Thank you for helping!
You can try
$storage = Storage::find($imageToDelete);
if($storage->delete()) {
return response()->json(['success' => 'success', 'message' => 'File Deleted']);
}
or
if(Storage::destroy($imageToDelete)) {
return response()->json(['success' => 'success', 'message' => 'File Deleted']);
}
or
$deleteImage = Storage::where('id', $imageToDelete)->delete();
if($deleteImage){
return response()->json(['success' => 'success', 'message' => 'File Deleted']);
}
You might need to check your image path. When you use laravel storage or file facde your file path should be absolute path for example public_path($imageToDelete)
I would like to attach an uploaded file (pdf) to a normal create function, but it doesn't really work. I have model, controller, form modified, and it says uploading, model validates, but still, no pdf in db. I have set field as blob. what can be wrong?
model:
use yii\web\UploadedFile;
class MyModel extends \yii\db\ActiveRecord {
public $file;
...
public function rules() {
return [
[['file'], 'file', 'skipOnEmpty' => true, 'extensions' => 'pdf'],
...
_form:
$form = ActiveForm::begin([
'enableClientValidation' => false,
'options' => ['enctype' => 'multipart/form-data'],
]
...
<?= $form->field($model, 'file')->fileInput() ?>
controller:
use yii\web\UploadedFile;
public function actionCreate() {
$model = new MyModel;
try {
if ($model->load($_POST)) {
$model->file = UploadedFile::getInstance($model, 'file');
if ($model->save()) {
...
This is how it works for file uploading
$model->file = UploadedFile::getInstance($model, 'file');//Get the uploaded file
$model->file->saveAs('uploads/' . $model->file->name);//Save uploaded file at your desired location
$user->image = $model->file->name;//image is my database field name,to only save the name of the uploaded file
I hope this will make it clear and solve your problem
So, I have a table that is auto-generated using DataTables. An action in my CakePHP grabs the data for that table, and formats it into JSON for datatables to use, this is the formatted JSON:
<?php
$data = array();
if (!empty($results)) {
foreach ($results as $result) {
$data[] = [
'name' => $result->name,
'cad' => $this->Number->currency($result->CAD, 'USD'),
'usd' => $this->Number->currency($result->USD, 'USD'),
'edit' => '<a href="' .
$this->Url->build(['controller' => 'Portfolios', 'action' => 'edit', $result->id]) .
'"><i class="fa fa-pencil"></i></a>',
'delete' => '<input type="checkbox" class="delete" value="' . $result->id . '">'
];
}
}
echo json_encode(compact('data'));
As you can see, I have a 'delete' option in there that outputs a checkbox with the value of the id of the corresponding element. When that checkbox is checked, a delete button is showing which sends this ajax request:
$('a#delete').on('click', function(e) {
e.preventDefault();
var checkedValues = [];
$('input.delete:checked').each(function() {
checkedValues.push($(this).val());
});
$.ajax({
url: $(this).attr('href'),
type: 'POST',
data: checkedValues
});
})
This ajax post goes to my controller action delete(). The problem I'm having is that I'm getting an error that states "Invalid Csrf Token". I know why this is happening, I'm submitting a form with Csrf protection on, that has no Csrf token added to it.
I can't figure out how to manually create a Csrf token for this situation (where the input values are generated after the page has loaded). Nor can I figure out how to disable Csrf protection. I read this, but the code is placed in the beforeFilter function, and as far as I understand it, that means it's run on every action, not just this one, and that's not what I want. Plus, to be completely honest, I would prefer a solution where I don't deactivate security functions.
Is there anyway to disable Csrf for this specific action, or is there a better way to do this?
read all about the CSRF component here
http://book.cakephp.org/3.0/en/controllers/components/csrf.html
you can disable for a specific action here:
http://book.cakephp.org/3.0/en/controllers/components/csrf.html#disabling-the-csrf-component-for-specific-actions
public function beforeFilter(Event $event) {
if (in_array($this->request->action, ['actions_you want to disable'])) {
$this->eventManager()->off($this->Csrf);
}
}
Above answer does not work in Cakephp 3.6 or later.
Cakephp add object of CsrfProtectionMiddleware in src/Application.php.
If you have to remove CSRF protection for specific controller or action then you can use following work around:
public function middleware($middlewareQueue)
{
$middlewareQueue = $middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(ErrorHandlerMiddleware::class)
// Handle plugin/theme assets like CakePHP normally does.
->add(AssetMiddleware::class)
// Add routing middleware.
// Routes collection cache enabled by default, to disable route caching
// pass null as cacheConfig, example: `new RoutingMiddleware($this)`
// you might want to disable this cache in case your routing is extremely simple
->add(new RoutingMiddleware($this, '_cake_routes_'));
/*
// Add csrf middleware.
$middlewareQueue->add(new CsrfProtectionMiddleware([
'httpOnly' => true
]));
*/
//CSRF has been removed for AbcQutes controller
if(strpos($_SERVER['REQUEST_URI'], 'abc-quotes')===false){
$middlewareQueue->add(new CsrfProtectionMiddleware([
'httpOnly' => true
]));
}
return $middlewareQueue;
}
So i needed a fix for cakephp 3.7 and using $_SERVER['REQUEST_URI'] is realllly not the way to go here. So here is how you are supposed to do it after reading through some documentation.
In src/Application.php add this function
public function routes($routes)
{
$options = ['httpOnly' => true];
$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware($options));
parent::routes($routes);
}
Comment out the existing CsrfProtectionMiddleware
public function middleware($middlewareQueue)
{
...
// $middlewareQueue->add(new CsrfProtectionMiddleware([
// 'httpOnly' => true
// ]));
}
Open your config/routes.php add $routes->applyMiddleware('csrf'); where you do want it
Router::prefix('api', function ($routes)
{
$routes->connect('/', ['controller' => 'Pages', 'action' => 'index']);
$routes->fallbacks(DashedRoute::class);
});
Router::scope('/', function (RouteBuilder $routes)
{
$routes->applyMiddleware('csrf');
$routes->connect('/', ['controller' => 'Pages', 'action' => 'dashboard']);
$routes->fallbacks(DashedRoute::class);
});
Note that my api user now has no csrf protection while the basic calls do have it.
If you have more prefixes don't forgot to add the function there aswell.
in Application.php this worked for me....
$csrf = new CsrfProtectionMiddleware();
// Token check will be skipped when callback returns `true`.
$csrf->whitelistCallback(function ($request) {
// Skip token check for API URLs.
if ($request->getParam('controller') === 'Api') {
return true;
}
});