Laravel resource update with upload file - php

I create a resource for NewProgram model, it works for READ, CREATE AND DELETE, but not for UPDATE, the problem is there is a file need to update too, here's my store function for example
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$program = new NewProgram;
$program->name = $request->name ?? '';
$program->description = $request->description ?? '';
$program->date_from = $request->date_from ?? '';
$program->date_to = $request->date_to ?? '';
$program->location = $request->location ?? '';
$program->organizer = $request->organizer ?? '';
if ($request->hasFile('image')) {
$attachment = $this->uploadAttachment($request->file('image'));
$program->image_url = route('image', $attachment->uuid);
}
$program->save();
return response()->json($program);
}
Yup, that's for store function and it works perfectly using postman, but here's the problem, I can update the data from NewProgram, but not for the file because the update function only receives a request from x-www-form-urlencoded not like store that receive from form-data, here's my code
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$program = NewProgram::find($id);
$program->name = $request->name ?? '';
$program->description = $request->description ?? '';
$program->date_from = $request->date_from ?? '';
$program->date_to = $request->date_to ?? '';
$program->location = $request->location ?? '';
$program->organizer = $request->organizer ?? '';
$program->image_url = $request->image_url ?? '';
$program->save();
return response()->json($program);
}
here's screenshot from postman
How to fix this? I want to make update function also update the file/attachment, not the string

This is almost a duplicate, there is a reason you can't use form-data on put requests, check this
but using x-www-form-urlencoded doesn't make it better
Just use post with form-data as some answers for the question in the link I gave suggest

Related

Laravel Pusher array_merge: Expected parameter 2 to be an array, null given

i'm following a tutorial from pusher to display notification on the website. Everything has been in line with the tutorial, however this particular error showed up when i try to access the notification on localhost:8000/test i have no clue on how to fix it.
the error message
expected result : notification send message
output : array_merge() error
related tutorial : https://pusher.com/tutorials/web-notifications-laravel-pusher-channels
related file : C:\xampp\htdocs\inventory-prototype\vendor\pusher\pusher-php-server\src\Pusher.php:518
here's my Events/ItemAdd :
class ItemAdd implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($user)
{
$this->user = $user;
$this->message = '{ $user } added an item';
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return ['item-add'];
}
}
here's my web.php:
Route::get('test', function () {
dd(event(new App\Events\ItemAdd('Someone')));
return "Event has been sent!";
});
vendor/pusher/src/Pusher.php -> Trigger
/**
* Trigger an event by providing event name and payload.
* Optionally provide a socket ID to exclude a client (most likely the sender).
*
* #param array|string $channels A channel name or an array of channel names to publish the event on.
* #param string $event
* #param mixed $data Event data
* #param array $params [optional]
* #param bool $already_encoded [optional]
*
* #throws PusherException Throws PusherException if $channels is an array of size 101 or above or $socket_id is invalid
* #throws ApiErrorException Throws ApiErrorException if the Channels HTTP API responds with an error
*
* #return object
*/
public function trigger($channels, $event, $data, $params = array(), $already_encoded = false)
{
if (is_string($channels) === true) {
$channels = array($channels);
}
$this->validate_channels($channels);
if (isset($params['socket_id'])) {
$this->validate_socket_id($params['socket_id']);
}
$has_encrypted_channel = false;
foreach ($channels as $chan) {
if (PusherCrypto::is_encrypted_channel($chan)) {
$has_encrypted_channel = true;
}
}
if ($has_encrypted_channel) {
if (count($channels) > 1) {
// For rationale, see limitations of end-to-end encryption in the README
throw new PusherException('You cannot trigger to multiple channels when using encrypted channels');
} else {
$data_encoded = $this->crypto->encrypt_payload($channels[0], $already_encoded ? $data : json_encode($data));
}
} else {
$data_encoded = $already_encoded ? $data : json_encode($data);
}
$query_params = array();
$path = $this->settings['base_path'].'/events';
// json_encode might return false on failure
if (!$data_encoded) {
$this->log('Failed to perform json_encode on the the provided data: {error}', array(
'error' => print_r($data, true),
), LogLevel::ERROR);
}
$post_params = array();
$post_params['name'] = $event;
$post_params['data'] = $data_encoded;
$post_params['channels'] = array_values($channels);
$all_params = array_merge($post_params, $params);
$post_value = json_encode($all_params);
$query_params['body_md5'] = md5($post_value);
$ch = $this->create_curl($this->channels_url_prefix(), $path, 'POST', $query_params);
$this->log('trigger POST: {post_value}', compact('post_value'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_value);
$response = $this->exec_curl($ch);
if ($response['status'] !== 200) {
throw new ApiErrorException($response['body'], $response['status']);
}
$result = json_decode($response['body']);
if (property_exists($result, 'channels')) {
$result->channels = get_object_vars($result->channels);
}
return $result;
}
any help will be appreciated
i say whatever, i just downgraded to pusher 4.1, on composer.json look for pusher and change the version to 4.1 in case anybody on earth other than me get the same error.
This error was resolved in the pusher-http-php library v5.0.1 and Laravel v8.29.0. https://github.com/pusher/pusher-http-php/issues/288
You can find the solution to this problem in this comment by ben-pusher to issue error array_merge - laravel 8 - php74 #278:
You may need to use composer require pusher/pusher-php-server ^4.1- support for v5.0.0 of this library hasn't been added to Laravel yet.

Magento2 addFieldToFilter call works with hardcoded value but not variable with same value

I'm building the admin for a Magento2 store (currently on 2.1.7, they want to use the newest version until we go live and then want to stabilize a particular version). The module in question is supposed to display all existing orders, with an actionsColumn that contains links to cancel, edit, and open a detailed overview of the purchased items associated with that order. The order detail page contains a grid view that should display all order items associated with an order number passed in the URL.
In order to filter out Order Items that don't relate to the specific Order Number, I've extended the \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult class. This works except for one weird caveat. If, in the addFieldToFilter call, I replace $ordNum with, say, '10000', it grabs the correct data. When using $ordNum to call this dynamically, however, it returns no rows at all. This despite trying all sorts of casting and === checks to ensure that there's no difference between the hardcoded and dynamic values. Is this a Magento bug? I can't at all figure out why this would be the case.
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$ordNum = $request->getParam('order_num');
$this->addFieldToFilter('order_num', ['eq' => $ordNum]); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
I just fixed it by using mentioned below steps
Store param value in session in controller
public function execute() {
$this->_catalogSession->setTokenId($this->request->getParam('entity_id'));
$this->_view->loadLayout();
$this->_view->loadLayoutUpdates();
$this->_view->getPage()->getConfig()->getTitle()->set(__('Redeem Token History'));
$this->_view->renderLayout();
}
Use session value in dataprovider
$tokensCollection->addFieldToFilter('token_id', ['eq' => $this->_catalogSession->getTokenId()]);
Enjoy :)
Try this in place of the getParam statement:
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
Just to make sure we are on the same page, this is the full code:
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
$this->addFieldToFilter('order_num', $ordNum); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
We have solved this issue by doing the following :
/**
* CcCustompriceProductListingDataProvider constructor.
* #param string $name
* #param string $primaryFieldName
* #param string $requestFieldName
* #param \Magento\Framework\Api\Search\ReportingInterface $reporting
* #param \Magento\Framework\Api\Search\SearchCriteriaBuilder $searchCriteriaBuilder
* #param \Magento\Framework\App\RequestInterface $request
* #param \Magento\Framework\Api\FilterBuilder $filterBuilder
* #param array $meta
* #param array $data
* #throws \Exception
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
ReportingInterface $reporting,
SearchCriteriaBuilder $searchCriteriaBuilder,
RequestInterface $request,
FilterBuilder $filterBuilder,
array $meta = [],
array $data = []
) {
$data['config']['filter_url_params']['product_id'] = $request->getParam('cppc_product_id', 0);
parent::__construct($name, $primaryFieldName, $requestFieldName, $reporting, $searchCriteriaBuilder, $request, $filterBuilder, $meta, $data);
}
You do not need to use any other function. The reason why this is is because it is also updated with an update URL and that does not have that parameter. By using adding that to the data it also parses that into the update url.
You can see that here (Parent function)
/**
* #return void
*/
protected function prepareUpdateUrl()
{
if (!isset($this->data['config']['filter_url_params'])) {
return;
}
foreach ($this->data['config']['filter_url_params'] as $paramName => $paramValue) {
if ('*' == $paramValue) {
$paramValue = $this->request->getParam($paramName);
}
if ($paramValue) {
$this->data['config']['update_url'] = sprintf(
'%s%s/%s/',
$this->data['config']['update_url'],
$paramName,
$paramValue
);
$this->addFilter(
$this->filterBuilder->setField($paramName)->setValue($paramValue)->setConditionType('eq')->create()
);
}
}
}

Simple PHP template class

I wanted separate my HTML from PHP so after searching I found a great little php class that just do the trick. Only issue that I try merging 2 templates together but it doesn’t work.
Here is the original class that i found from below website
http://www.broculos.net/2008/03/how-to-make-simple-html-template-engine.html#.WCsa8CTy2ng
class Template {
/**
* The filename of the template to load.
*
* #access protected
* #var string
*/
protected $file;
/**
* An array of values for replacing each tag on the template (the key for each value is its corresponding tag).
*
* #access protected
* #var array
*/
protected $values = array();
/**
* Creates a new Template object and sets its associated file.
*
* #param string $file the filename of the template to load
*/
public function __construct($file) {
$this->file = $file;
}
/**
* Sets a value for replacing a specific tag.
*
* #param string $key the name of the tag to replace
* #param string $value the value to replace
*/
public function set($key, $value) {
$this->values[$key] = $value;
}
/**
* Outputs the content of the template, replacing the keys for its respective values.
*
* #return string
*/
public function output() {
/**
* Tries to verify if the file exists.
* If it doesn't return with an error message.
* Anything else loads the file contents and loops through the array replacing every key for its value.
*/
if (!file_exists($this->file)) {
return "Error loading template file ($this->file).<br />";
}
$output = file_get_contents($this->file);
foreach ($this->values as $key => $value) {
$tagToReplace = "[#$key]";
$output = str_replace($tagToReplace, $value, $output);
}
return $output;
}
/**
* Merges the content from an array of templates and separates it with $separator.
*
* #param array $templates an array of Template objects to merge
* #param string $separator the string that is used between each Template object
* #return string
*/
static public function merge($templates, $separator = "\n") {
/**
* Loops through the array concatenating the outputs from each template, separating with $separator.
* If a type different from Template is found we provide an error message.
*/
$output = "";
foreach ($templates as $template) {
$content = (get_class($template) !== "Template")
? "Error, incorrect type - expected Template."
: $template->output();
$output .= $content . $separator;
}
return $output;
}
}
Code that work
$post = new Template("post.tpl");
$post->set("post_title", $post_title);
$post->set("post_description", $post_description);
$post->set("content", $post->output());
echo $post->output();
Even when I want to loop if I add the code it works fine. But then I try to merge two template files together
all_posts.tpl
<div class=”posts”>
<h1>[#page_title]</</h1>
[#display_posts]
</div>
display_posts.tpl
<div class=”post”>
<h2>[#display_title]</h2>
<p>[#display_description]</p>
</div>
So what I want to do now is to push display_posts.tpl to all_posts.tpl and replace the tag [#display_posts]
So in my php I did below
$post = new Template("all_posts.tpl ");
$post->set("page_title", "All Posts");
echo $post->output();
//created a array from a mysqli loop
$post_set = array();
while ($post_row = mysqli_fetch_array($posts)){
$post_title = $post_row['title'];
$post_description = $post_row['description'];
$post_set[] = array(
"display_title" => $post_title,
"display_description" => $post_description
);
}
foreach ($post_set as $posts) {
$row = new Template("list_users_row.tpl");
foreach ($posts as $key => $value) {
$row->set($key, $value);
}
$postsTemplates[] = $posts;
}
// here i try to merge template
$postsContents = Template::merge($postsTemplates);
$layout->set("content", $postsContents->output());
echo $layout->output();
But this is throwing off a error set() is not an function. Could someone help me out to figure out this? I’m still in the process of learning php classes.
The reason why $layout->set() does not work is because $layout is not a Template Object. In your code you use $post = new Template("all_posts.tpl ");. This makes $post a Template object that can use the set() function in your Template class.
So you should do: $layout = new Template(....) and than you can call $layout->set().
In the tutorial they do the same:
include("template.class.php");
$profile = new Template("user_profile.tpl");
$profile->set("username", "monk3y");
$profile->set("photoURL", "photo.jpg");
$profile->set("name", "Monkey man");
$profile->set("age", "23");
$profile->set("location", "Portugal");
$layout = new Template("layout.tpl");
$layout->set("title", "User profile");
$layout->set("content", $profile->output());
echo $layout->output();
Hope this helps.

Check whether parameter exists or not in the request

I have this function:
/**
* #Secure(roles="IS_AUTHENTICATED_FULLY")
* #Route("/chequearFabricanteDistribuidor", name="chequearFabricanteDistribuidor", condition="request.headers.get('X-Requested-With') == 'XMLHttpRequest'")
* #Method("GET")
*/
public function chequearFabricanteAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('AppBundle:FabricanteDistribuidor')->findOneBy(
array( 'nombre' => $request->query->get('fabricante')['nombre'] )
);
$response['valid'] = true;
if ($entities) {
$response['valid'] = false;
}
return new JsonResponse($response);
}
The function needs to be called from two different forms and the only different is the request var that holds the value. In the first form is: $request->query->get('fabricante')['nombre'] while in the second is $request->query->get('distribuidor')['nombre'] so I'm asking if the right way to handle this could be:
if (isset($request->query->get('fabricante')))
{
$nombre = $request->query->get('fabricante')['nombre'];
}
else
{
$nombre = $request->query->get('distribuidor')['nombre'];
}
Is this right? Exists a better one?
As Cerad posted on the responses, you could use:
$request->query->has('distribuidor')

Most efficient way to tell if an entity exists in Doctrine 2

I'm creating a registration form and want to check to see if an email is not already associated with an account. All the ways I can see will create the entire User entity, but I just need to know if it exists.
public function isUnusedEmail($email) {
$em = static::$pimple['em'];
$dql = 'SELECT 1 FROM App\Model\User user WHERE user.email = :email';
$query = $em->createQuery($dql);
$query->setParameter('email', $email);
$res = $query->getResult();
return empty($res);
}
Consider the following method:
/**
* #param string $token
* #return bool
*/
public function isTokenUnique($token)
{
$manager = $this->getEntityManager();
/** #var Doctrine\ORM\Query $query */
$query = $manager->
createQuery('SELECT 1 FROM AppBundle:Member m WHERE m.token = :token')
->setParameter('token', $token)
->setMaxResults(1)
;
return (count($query->getResult()) == 0);
}
Notice a call to setMaxResults(). It can be important in some cases.

Categories