Is there a way to set flash message or error message from the Model, in the beforeSave function and read the error/message in the view. And I'm not talking about validation errors.
Something along these lines should work with the information available on hand:
<?php
class AppModel extends Model {
public $lastErrorMessage;
public function beforeSave(...) {
$this->lastErrorMessage = null;
return true;
}
}
<?php
class MyModel Extends AppModel {
public function beforeSave(...) {
parent::beforeSave(..);
if (error) {
$this->lastErrorMessage = 'Some error message';
return false;
}
return true;
}
}
<?php
class MyController extends AppController {
public function action() {
if ($this->MyModel->save($this->request->data)) {
} else {
$message = "Some default message";
if ($this->MyModel->lastErrorMessage) {
$message = $this->MyModel->lastErrorMessage;
}
$this->Session->setFlash($message);
}
}
}
Related
I'm working on a website where people can download wallpapers.
I have a table 'Images' with a 'Download' column.
I would like to increment the value on this field for each time people click on the 'download button'
I'm usingg Symphony 6 with EasyAdmin-bundle & Twig
Can someone help me to make a query to increment this value ?
Thank you
enter image description here
<?php
namespace App\Entity;
use App\Repository\ImagesRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ImagesRepository::class)]
class Images
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column(length: 255)]
private ?string $image = null;
#[ORM\Column(nullable: true)]
private ?int $downloads = null;
#[ORM\ManyToOne(inversedBy: 'images')]
private ?Wallpapers $wallpaper = null;
#[ORM\ManyToOne(inversedBy: 'device')]
private ?Devices $devices = null;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(string $image): self
{
$this->image = $image;
return $this;
}
public function getDownloads(): ?int
{
return $this->downloads;
}
public function setDownloads(?int $downloads): self
{
$this->downloads = $downloads;
return $this;
}
public function getWallpaper(): ?Wallpapers
{
return $this->wallpaper;
}
public function setWallpaper(?Wallpapers $wallpaper): self
{
$this->wallpaper = $wallpaper;
return $this;
}
public function getDevices(): ?Devices
{
return $this->devices;
}
public function setDevices(?Devices $devices): self
{
$this->devices = $devices;
return $this;
}
public function __toString(): string
{
return $this->image;
}
}
<div class="row">
{% for item in wallpapers.getImages() %}
<div class="col">
<img src="/uploads/wps/{{ item.image }}" class="card"/>
<button>Download</button>
</div>
{% endfor %}
</div>
I tried many example but as it's my first experience with Symphony is a bit complicated to understand all the concept of Query Builder
You must create a new controller with route like this:
#[Route('/downloads/{entity}', name:'downloads')
public function downloads(
Entity $entity,
EntityManagerInterface $em
){
$entity->setDownload($entity->getDownload() + 1);
$em->persist($entity);
$em->flush();
return $this->file($entity->getImage(), 'name_file.ext');
}
in your twig file you can redirect to this func with entity id param.
Function is an example you must use your entity class and get file content to download something.
I have more function where I need to read $data['getContacts'] more times, the code working correctly, but there is a clean and different method for call it?
thanks!
class AppController extends CI_Controller {
public $id;
function __construct() {
parent::__construct();
$this->id = !empty($this->input->post('id')) ? (int)$this->input->post('id', TRUE) : '';
}
public function restoreCredit()
{
$data['getContacts'] = $this->appmodel->getContacts($this->id); //repeat here?
if($data['getContacts']->status != false) :
$this->appmodel->restoreCredit($this->id);
endif;
}
public function createRandToken()
{
$data['getContacts'] = $this->appmodel->getContacts($this->id); //repeat here?
if(!empty($data['getContacts']) && $data['getContacts']->token == false):
$this->appmodel->setRandUserToken($this->id);
endif;
}
}
Your could define a function getContacts. It will fetch $contacts first time from the DB, after that it will always returned the fetched Contacts.
<?php
class AppController extends CI_Controller
{
public $id;
public $contacts;
function __construct()
{
parent::__construct();
$this->id = !empty($this->input->post('id')) ? (int) $this->input->post('id', TRUE) : '';
}
public function getContacts() {
if( !empty ( $this->contacts) ) { //If its populated return from here.
return $this->contacts;
}
$this->contacts = $this->appmodel->getContacts($this->id);
return $this->contacts;
}
public function restoreCredit()
{
$data['getContacts'] = $this->getContacts();
if ($data['getContacts']->status != false) :
$this->appmodel->restoreCredit($this->id);
endif;
}
public function createRandToken()
{
$data['getContacts'] = $this->getContacts();
if (!empty($data['getContacts']) && $data['getContacts']->token == false) :
$this->appmodel->setRandUserToken($this->id);
endif;
}
}
I am having issue with the route and controller.
The error code consist of sql column not found which is looking for column id from items table. Which im quite curious due to the differences with my migration.
CartController.php
namespace App\Http\Controllers;
use App\Cart;
use App\CartItem;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class CartController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function addItem ($itemNo){
$cart = Cart::where('user_id',Auth::user()->id)->first();
if(!$cart){
$cart = new Cart();
$cart->user_id=Auth::user()->id;
$cart->save();
}
$cartItem = new Cartitem();
$cartItem->itemNo=$itemNo;
$cartItem->cart_id= $cart->id;
$cartItem->save();
return redirect('/cart');
}
public function showCart(){
$cart = Cart::where('user_id',Auth::user()->id)->first();
if(!$cart){
$cart = new Cart();
$cart->user_id=Auth::user()->id;
$cart->save();
}
$items = $cart->cartItems;
$total=0;
foreach($items as $item){
$total+=$item->product->price;
}
return view('cart.view',['items'=>$items,'total'=>$total]);
}
public function removeItem($id){
CartItem::destroy($id);
return redirect('/cart');
}
}
ItemController.php
<?php
namespace App\Http\Controllers;
use App\Item;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\File;
class ItemController extends Controller
{
public function index(){
$items = item::all();
return view('admin.items',['items' => $items]);
}
public function destroy($itemNo){
item::destroy($itemNo);
return redirect('/admin/items');
}
public function newItem(){
return view('admin.new');
}
public function add() {
$file = Request::file('file');
$extension = $file->getClientOriginalExtension();
Storage::disk('local')->put($file->getFilename().'.'.$extension, File::get($file));
$entry = new \App\File();
$entry->mime = $file->getClientMimeType();
$entry->original_filename = $file->getClientOriginalName();
$entry->filename = $file->getFilename().'.'.$extension;
$entry->save();
$Item = new Item();
$Item->file_id=$entry->id;
$Item->itemName =Request::input('name');
$Item->itemDescription =Request::input('description');
$Item->price =Request::input('price');
$Item->imageurl =Request::input('imageurl');
$Item->save();
return redirect('admin/items');
}
}
I have a controller where I throw a custom exception and I have a custom exception renderer class, which extends the basic exception renderer.
Now when I throw the exception, I'd like to do some cleanup with the stuff, that went wrong and after that render a custom error page.
class AppExceptionRenderer extends ExceptionRenderer {
public function invalidCall($error) {
$this->controller->render('/Errors/invalid_call');
$this->controller->response->send();
}
public function incompleteCall($error) {
$this->controller->render('/Errors/incomplete_call');
$this->controller->response->send();
}
}
The rendering works well so far. But where should I put the logic for the cleanup things?
In the exception itself? In the renderer? In the controller before throwing the exception?
Well, as so often there are many ways to skin a cat, but I'd say in order to stay DRY, for easy testing, and in order to stay in compliance with the recommended fat model concept, you should put the logic in the model.
And in order to decouple cleanup and exception handling, you could for example utilize the event system and let the models that may need to be cleaned attach themselfs as listeners (they should know best whether they may need to be cleaned up), and let a custom error handler dispatch an appropriate event, that way the exception handler doesn't need to know about the app internals.
Here's some very basic, untested example code that should illustrate the idea:
<?php
App::uses('CakeEventManager', 'Event');
class ExampleModel extends AppModel
{
public $name = 'Example';
public function __construct($id = false, $table = null, $ds = null)
{
CakeEventManager::instance()->attach(array($this, 'cleanup'), 'AppErrorHandler.beforeHandleException');
parent::__construct($id, $table, $ds);
}
public function cleanup()
{
// do some magic
}
}
?>
<?php
App::uses('CakeEvent', 'Event');
App::uses('CakeEventManager', 'Event');
class AppErrorHandler extends ErrorHandler
{
public static function handleException(Exception $exception)
{
CakeEventManager::instance()->dispatch(new CakeEvent('AppErrorHandler.beforeHandleException', get_called_class(), array($exception)));
parent::handleException($exception);
}
}
?>
Update
In order to be able to react to specific exceptions only, you could for example utilize the exception class name in the event name, so it would trigger events like ...beforeHandleFooBarException to wich you could explicitly subscribe:
<?php
class AppErrorHandler extends ErrorHandler
{
public static function handleException(Exception $exception)
{
CakeEventManager::instance()->dispatch(new CakeEvent('AppErrorHandler.beforeHandle' . get_class($exception), get_called_class(), array($exception)));
parent::handleException($exception);
}
}
?>
<?php
class ExampleModel extends AppModel
{
public $name = 'Example';
public function __construct($id = false, $table = null, $ds = null)
{
$eventManager = CakeEventManager::instance();
$callback = array($this, 'cleanup');
$eventManager->attach($callback, 'AppErrorHandler.beforeHandleInvalidCallException');
$eventManager->attach($callback, 'AppErrorHandler.beforeHandleIncompleteCallException');
parent::__construct($id, $table, $ds);
}
public function cleanup()
{
// do some magic
}
}
?>
If you would stick with the generic exception event, then another option would be to check the type of the exception in the models event listener callback:
public function __construct($id = false, $table = null, $ds = null)
{
CakeEventManager::instance()->attach(array($this, 'beforeHandleException'), 'AppErrorHandler.beforeHandleException', array('passParams' => true));
parent::__construct($id, $table, $ds);
}
public function beforeHandleException($exception)
{
if($exception instanceof InvalidCallException ||
$exception instanceof IncompleteCallException)
{
$this->cleanup();
}
}
public function cleanup()
{
// do some magic
}
I am using Cakephp 2.3.0, loading following component.
class BreadCrumbsComponent extends Component {
public $components = array();
public $controller = null;
public function initialize($controller) {
}
public function startup($controller) {
$this->controller = $controller;
}
public function beforeRender($controller) {
}
public function shutDown($controller) {
}
public function beforeRedirect($controller, $url, $status = null, $exit = true) {
}
public function handle($controllerName = NULL, $actionName = NULL) {
pr($this->controller->modelClass);
}
}
It get error following error
Trying to get property of non-object [APP\Controller\Component\BreadCrumbsComponent.php, line 38]
I am unable to access $this->controller there. Any reason? How do I make it work?
Read here startup method is called after the controller so need to initialize controller in initialize method as below,
public function initialize(&$controller, $settings = array()) {
$this->controller = $controller;
}