I'm trying to create a simple shopping cart and have an addToCart function where it's meant to do what is says.
/**
* Creates the option to 'add product to cart'.
*
* #Route("/{id}/addToCart", name="product_addToCart")
* #Method("GET")
* #Template()
*/
public function addToCartAction(Request $request, $id) {
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('ShopTestBundle:Product')->find($id);
$cartArray = $this->setEmptyCartArray();
if ($this->checkUserLogin()) {
$this->addFlash('notice', 'Login to create a cart');
} else {
// $cartArray[] = [$product->getId(), $product->getName(), $product->getPrice()];
$cartArray[$product->getId()] = [$product->getName(), $product->getPrice()];
//think of a way to do this without using product
// $cartArray = $this->setCartArray($product); //SAME RESULT----------------------------
// $this->setCartArray($cartArray);
// var_dump($cartArray); die;
// $cartArray = $this->putInGetCartArray(); //SAME RESULT--------------------------------
$this->putInGetCartArray($cartArray);
// var_dump($cartArray); die;
$this->addFlash('notice', 'The product: '.$product->getName().' has been added to the cart!');
}
return $this->redirectToRoute('product');
}
I need that array to go to my showCart function where I show the user what they placed in their cart.
/**
* Shows Empty Cart
*
* #Route("/showCart", name="product_showCart")
* #METHOD("GET")
* #Template()
*/
public function showCartAction(Request $request) {
$em = $this->getDoctrine()->getManager();
// $id = ;
$product = $em->getRepository('ShopTestBundle:Product')->find($id);
$user = $this->getUser();
$totalCostOfAllProducts = 0;
// $cartArray = $this->setEmptyCartArray();
// $cartArray =
// $cartArray[] = [];
$this->setCartArray($product);
$this->putInGetCartArray($cartArray);
// $totalCostOfAllProducts = $this->getTotalCost($em, $cartArray, $totalCostOfAllProducts);
if (empty($price) && empty($quantity) && empty($totalCostOfAllProducts) && empty($cartArray)) {
$price = 0; $quantity = 0; $totalCostOfAllProducts = 0; $cartArray = [];
}
return array(
'user' => $user,
// 'qaunt' => $quant,
'cartArray' => $cartArray,
'totalCostOfAllProducts' => $totalCostOfAllProducts,
);
}
(btw I know this produces an error as it stands now...$id undefined...)
I've created a couple different helper methods within the controller but I can't seem to get everything working together right. I get an $id in addToCart but in showCart I do not and that complicates things because I can't simply set the array again as I need the $id, $name, and $price from my Entity.
/*---------------------------------------------------------------------------------------------------*/
private function setEmptyCartArray() {
$cartArray = array();
return $cartArray;
}
/*---------------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/
// private function setCartArray($em, $id) {
// // $cartArray = array();
// // var_dump($cartArray); die;
// // foreach ($cartArray as $key => $value) {
// $product = $em->getRepository('ShopTestBundle:Product')->find($id);
// // $id = $productID->getId();
// // var_dump($id); die;
// $cartArray[$product->getId()] = [$product->getName(), $product->getPrice()];
// // $cartArray[] = [$productID->getName(), $productID->getPrice()];
// // }
// return $this;
// }
/*---------------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/
private function setCartArray($product) {
// $this->cartArray = $cartArray;
$cartArray[$product->getId()] = [$product->getName(), $product->getPrice()];
// $cartArray[] = [$productID->getName(), $productID->getPrice()];
// }
// return $this;
return $cartArray;
}
/*---------------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/
private function putInGetCartArray($cartArray) {
// $cartArray = $this->setCartArray ($product);
// $cartArray = $this->setCartArray($cartArray);
$userCartArray = $cartArray;
// return $this->cartArray;
return $userCartArray;
}
/*---------------------------------------------------------------------------------------------------*/
If you have any questions or need more clarity, I'd be happy to provide it.
I don't think you'll be able to have a functioning shopping cart without changing a lot of your code since it doesn't look like you're actually storing any data anywhere, I mean that you should probably keep track of your cart's content using sessions or database or whatever, which doesn't seem to be the case here.
Anyways, let's say you just want to show your cart's content once, and let it get reset on the next request, you could either pass your array as an argument to the showCartAction function, something like this :
public function addToCartAction(Request $request, $id) {
// ...
else {
$cartArray[$product->getId()] = [$product->getName(), $product->getPrice()];
$this->addFlash('notice', 'The product: '.$product->getName().' has been added to the cart!');
$this->showCartAction($request, $cartArray);
}
// ...
}
// ...
public function showCartAction(Request $request, $cart = array()) {
// ...
}
Or you could also store the array as one of your controller's property, like this :
class CartController extends Controller {
private $cartArray = array();
public function addToCartAction(Request $request, $id) {
// ...
else {
// Set the property here
$this->cartArray[$product->getId()] = [$product->getName(), $product->getPrice()];
}
// ...
}
// ...
public function showCartAction(Request $request) {
return array(
'user' => $user,
'cartArray' => $this->cartArray, // Access the array here
'totalCostOfAllProducts' => $totalCostOfAllProducts,
);
}
}
Hope this helps a little.
Related
This piece of code shows a smll part of the models post.php from October Rainlab Blog plugin. The AfterSave() function is modified, it sends an e-mail when a new blogPost in the backend is saved by the administrator, however, I would like to send it when it is actually Published and make sure it is not sending multiple times. How could I accomplish this?
public function filterFields($fields, $context = null)
{
if (!isset($fields->published, $fields->published_at)) {
return;
}
$user = BackendAuth::getUser();
if (!$user->hasAnyAccess(['rainlab.blog.access_publish'])) {
$fields->published->hidden = true;
$fields->published_at->hidden = true;
}
else {
$fields->published->hidden = false;
$fields->published_at->hidden = false;
}
}
public function afterValidate()
{
if ($this->published && !$this->published_at) {
throw new ValidationException([
'published_at' => Lang::get('rainlab.blog::lang.post.published_validation')
]);
}
}
public function beforeSave()
{
if (empty($this->user)) {
$user = BackendAuth::getUser();
if (!is_null($user)) {
$this->user = $user->id;
}
}
$this->content_html = self::formatHtml($this->content);
}
public function afterSave()
{
$user = BackendAuth::getUser();
if ($user && $user->hasAnyAccess(['rainlab.blog.access_publish'])) {
$susers = Db::select('select * from users where is_activated = ?', [1]);
foreach ($susers as $suser) {
$currentPath = $_SERVER['PHP_SELF'];
$pathInfo = pathinfo($currentPath);
$hostName = $_SERVER['HTTP_HOST'];
$protocol = strtolower(substr($_SERVER["SERVER_PROTOCOL"],0,5))=='https'?'https':'http';
$protocol.'://'.$hostName.$pathInfo['dirname']."/";
$spost_url = $protocol.'://'.$hostName.$pathInfo['dirname']."/"."nieuws/".$this->attributes['slug'] ;
$stitle = $this->attributes['title'] ;
$body = '<div> Hallo '.$suser->name.'</br> Er is zojuist een nieuws bericht gepubliceerd voor alle leden van mycompany.nl , je kunt hier het bericht lezen aangaande: '.$stitle.' </div>' ;
//$from = $user->email ;
$from = 'noreply#mycompany.nl';
$headers = "From: $from\r\n";
$headers .= "Content-type: text/html\r\n";
mail($suser->email,'Nieuws van mycompany', $body,$headers);
}
}
}
/**
* Sets the "url" attribute with a URL to this object.
* #param string $pageName
* #param Controller $controller
* #param array $params Override request URL parameters
*
* #return string
*/
public function setUrl($pageName, $controller, $params = [])
{
$params = array_merge([
'id' => $this->id,
'slug' => $this->slug,
], $params);
if (empty($params['category'])) {
$params['category'] = $this->categories->count() ? $this->categories->first()->slug : null;
}
// Expose published year, month and day as URL parameters.
if ($this->published) {
$params['year'] = $this->published_at->format('Y');
$params['month'] = $this->published_at->format('m');
$params['day'] = $this->published_at->format('d');
}
return $this->url = $controller->pageUrl($pageName, $params);
}
/**
* Used to test if a certain user has permission to edit post,
* returns TRUE if the user is the owner or has other posts access.
* #param User $user
* #return bool
*/
public function canEdit(User $user)
{
return ($this->user_id == $user->id) || $user->hasAnyAccess(['rainlab.blog.access_other_posts']);
}
public static function formatHtml($input, $preview = false)
{
$result = Markdown::parse(trim($input));
// Check to see if the HTML should be cleaned from potential XSS
$user = BackendAuth::getUser();
if (!$user || !$user->hasAccess('backend.allow_unsafe_markdown')) {
$result = Html::clean($result);
}
if ($preview) {
$result = str_replace('<pre>', '<pre class="prettyprint">', $result);
}
$result = TagProcessor::instance()->processTags($result, $preview);
return $result;
}
//
// Scopes
//
public function scopeIsPublished($query)
{
return $query
->whereNotNull('published')
->where('published', true)
->whereNotNull('published_at')
->where('published_at', '<', Carbon::now())
;
}
/**
* Lists posts for the frontend
*
* #param $query
* #param array $options Display options
* #return Post
*/
public function scopeListFrontEnd($query, $options)
{
/*
* Default options
*/
extract(array_merge([
'page' => 1,
'perPage' => 30,
'sort' => 'created_at',
'categories' => null,
'exceptCategories' => null,
'category' => null,
'search' => '',
'published' => true,
'exceptPost' => null
], $options));
$searchableFields = ['title', 'slug', 'excerpt', 'content'];
if ($published) {
$query->isPublished();
}
One way to accomplish this would be to extend the Post model.
As an example, you create a new plugin and model with an is_notified field.
You would then add something like this to the boot() method of your new plugin:
PostModel::extend(function ($model) {
$model->hasOne['your_model'] = ['Author\PluginName\Models\YourModel'];
});
PostsController::extendFormFields(function ($form, $model, $context) {
// Checking for Post instance
if (!$model instanceof PostModel) {
return;
}
// without this code you can get an error saying "Call to a member function hasRelation() on null"
if (!$model->your_model) {
$model->your_model = new YourModel;
}
}
You can then use that new model in the afterSave method
public function afterSave()
{
$user = BackendAuth::getUser();
if ($user && $user->hasAnyAccess(['rainlab.blog.access_publish'])) {
$susers = Db::select('select * from users where is_activated = ?', [1]);
foreach ($susers as $suser) {
...
if ($this->your_model->is_notified != true) {
mail($suser->email,'Nieuws van mycompany', $body,$headers);
$this->your_model->is_notified = true;
}
}
}
}
You should also consider using the extend method instead of modifying 3rd party plugin code. This will allow you to update the plugin without losing your edits. Something like this:
PostModel::extend(function ($model) {
$model->hasOne['your_model'] = ['Author\PluginName\Models\YourModel'];
// You can transfer your afterSave code here!
$model->bindEvent('model.afterSave', function () use ($model) {
$user = BackendAuth::getUser();
if ($user && $user->hasAnyAccess(['rainlab.blog.access_publish'])) {
..
}
});
});
Let me know if you have any questions!
I am creating a sponsorship system and I would like to display the names of the godchildren in a list.
I have a list of'ambassadors' whose ambassadors will have godchildren who will have other godchildren. At the moment I manage to display the number of godchildren for each ambassador, but I have trouble creating the script to display the name. Could you help me? Thank you.
public function adminAmbassadorsAction(Request $request)
{
/** #var UserManager $userManager * */
$ambassadors = $this->get('fos_user.user_manager');
$em = $this->getDoctrine()->getEntityManager(); # Getting the entity manager for future request.
/** #var UserRepository $repository * */
$repository = $em->getRepository('AppBundle:User');
// Recup all ambassadors
$ambassadors = $repository->findByRole("ROLE_AMBASSADOR");
$proprietesAmbassadeur = array();
// Iterate ambassadors
foreach ($ambassadors as $ambassador) {
// Recup all fileuls
// $ambassador = $this->getFullName(); // recup name parrain
$filleuls = $repository->findBy(array('parrain' => $ambassador->getId()));
foreach ($filleuls as $filleul) {
($filleul->getFullName());
}
$proprietesAmbassadeur[$ambassador->getId()] = count($filleuls);
}
// render the view
return $this->render('Admin/listing_sponsor.html.twig', [
'os_ambassadors' => $ambassadors,
'os_proprietes' => $proprietesAmbassadeur,
] );
}
}
You are not passing the god childs to the view.
I've rewritten your code so it passes the total of god childs and all their names.
public function adminAmbassadorsAction(Request $request)
{
/** #var UserManager $userManager * */
$ambassadors = $this->get('fos_user.user_manager');
$em = $this->getDoctrine()->getEntityManager(); # Getting the entity manager for future request.
/** #var UserRepository $repository * */
$repository = $em->getRepository('AppBundle:User');
// Recup all ambassadors
$ambassadors = $repository->findByRole("ROLE_AMBASSADOR");
$proprietesAmbassadeur = array();
// Iterate ambassadors
foreach ($ambassadors as $ambassador) {
// Recup all fileuls
// $ambassador = $this->getFullName(); // recup name parrain
$filleuls = $repository->findBy(array('parrain' => $ambassador->getId()));
$proprietesAmbassadeur[$ambassador->getId()] = array('total' => count($filleuls), 'filleulFullNames' => array());
foreach ($filleuls as $filleul) {
$proprietesAmbassadeur[$ambassador->getId()]['filleulFullNames'][] = $filleul->getFullName();
}
}
// render the view
return $this->render('Admin/listing_sponsor.html.twig', [
'os_ambassadors' => $ambassadors,
'os_proprietes' => $proprietesAmbassadeur,
] );
}
I want to add a product to my cart But for that i want to logged in the user.Means after he logged in only he can see his cart. But after logged in if he sees the cart, he should see the the product just he added to cart but he saw the old one.Though I put the url on session in controller.How do i make it correct?
Here is show Login form :
public function showLoginForm()
{
Session::put('url.intended',\URL::previous());
return view('cart.login');
}
Add to Cart controller :
public function addCart(Request $request)
{
if(Auth::check())
{
$name = $request->name;
$product = Product::where('name' , '=', $name)->first();
$product_id = $product->id;
$product = DB::table('carts')
->where('carts.product_id','=',$product_id)
->where('carts.status','=',1)
->select('carts.product_id')
->first();
if(!$product){
$cart = new Cart();
$checkBox = Input::get('additionals');
if (is_array($checkBox)){
$cart->additionals = array_sum($checkBox);
}
else {
$cart->additionals =0;
}
$cart->user_id =Auth::user()->id;
$name = $request->name;
$product = Product::where('name' , '=', $name)->first();
$cart->product_id = $product->id;
// $product = Product::find($cart->product_id);
$cart->price =$product->price;
$cart->status = 1;
$cart->save();
return redirect('shop-cart');
}
else {
return redirect('shop-cart');
}
}
else{
return redirect('/login');
}
}
And Login controller :
protected function login(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required',
]);
if (auth()->attempt(array('email' => $request->input('email'),
'password' => $request->input('password'))))
{
if(auth()->user()->is_activated == '0'){
Auth::logout();
return back()->with('warning',"First please active your account.");
}
else{
return Redirect::to(Session::get('url.intended'));
}
}
else{
return back()->with('error','your username and password are wrong.');
}
}
Add to Cart Route:
Route::get('add-to-cart/{name}','CartController#addCart');
public function addcart(Request $request)
{
$id=$request->id; $time=60*24*14; /*60 * 24 * 14 = 14 drays 60=minutes 24=hours 14=days*/
$value=0;
if( Cookie::get('cart')!==null ){
$anonim=Cookie::get('cart');
DB::table("cart")->insert(["anonim"=>$anonim,"product_id"=>$id]);
return 0;
}else{
$value=DB::table("cart")->max("anonim")+1;
if(empty($value)){
$value=0;
}
DB::table("cart")->insert(["anonim"=>$value,"product_id"=>$id]);
$cookie = cookie('cart', $value, $time);
return response()->cookie($cookie);
}
}
I am trying to add extra fields in magento product review.
1. Good thing about product
2. Bad thing about product
I follow this link. I have override core files into my local files.
Here my code:
template/review/form.phtml
<li>
<label for="goodthings" class="required"><?php echo $this->__('Good Things') ?></label>
<div class="input-box">
<textarea name="goodthings" id="goodthings" cols="5" rows="3" ><?php echo $this->escapeHtml($data->getGoodthings()) ?></textarea>
</div>
</li>
<li>
<label for="badthings" class="required"><?php echo $this->__('Bad Things') ?></label>
<div class="input-box">
<textarea name="badthings" id="badthings" cols="5" rows="3" ><?php echo $this->escapeHtml($data->getBadthings()) ?></textarea>
</div></li>
and
/local/Mage/Review/Model/Resource/Review.php
$detail = array(
'title' => $object->getTitle(),
'detail' => $object->getDetail(),
'nickname' => $object->getNickname(),
'goodthing' => $object->getGoodthings(),
'badthing' => $object->getBadthings(),
);
Database query:
ALTER TABLE `review_detail` ADD `goodthing` TEXT NOT NULL COMMENT 'review goodthing' AFTER `nickname` ,
ADD `badthing` TEXT NOT NULL COMMENT 'review badthing' AFTER `goodthing` ;
/local/Mage/Review/controllers/ProductController.php
class Mage_Review_ProductController extends Mage_Core_Controller_Front_Action
{
/**
* Action list where need check enabled cookie
*
* #var array
*/
protected $_cookieCheckActions = array('post');
public function preDispatch()
{
parent::preDispatch();
$allowGuest = Mage::helper('review')->getIsGuestAllowToWrite();
if (!$this->getRequest()->isDispatched()) {
return;
}
$action = strtolower($this->getRequest()->getActionName());
if (!$allowGuest && $action == 'post' && $this->getRequest()->isPost()) {
if (!Mage::getSingleton('customer/session')->isLoggedIn()) {
$this->setFlag('', self::FLAG_NO_DISPATCH, true);
Mage::getSingleton('customer/session')->setBeforeAuthUrl(Mage::getUrl('*/*/*', array('_current' => true)));
Mage::getSingleton('review/session')->setFormData($this->getRequest()->getPost())
->setRedirectUrl($this->_getRefererUrl());
$this->_redirectUrl(Mage::helper('customer')->getLoginUrl());
}
}
return $this;
}
/**
* Initialize and check product
*
* #return Mage_Catalog_Model_Product
*/
protected function _initProduct()
{
Mage::dispatchEvent('review_controller_product_init_before', array('controller_action'=>$this));
$categoryId = (int) $this->getRequest()->getParam('category', false);
$productId = (int) $this->getRequest()->getParam('id');
$product = $this->_loadProduct($productId);
if (!$product) {
return false;
}
if ($categoryId) {
$category = Mage::getModel('catalog/category')->load($categoryId);
Mage::register('current_category', $category);
}
try {
Mage::dispatchEvent('review_controller_product_init', array('product'=>$product));
Mage::dispatchEvent('review_controller_product_init_after', array(
'product' => $product,
'controller_action' => $this
));
} catch (Mage_Core_Exception $e) {
Mage::logException($e);
return false;
}
return $product;
}
/**
* Load product model with data by passed id.
* Return false if product was not loaded or has incorrect status.
*
* #param int $productId
* #return bool|Mage_Catalog_Model_Product
*/
protected function _loadProduct($productId)
{
if (!$productId) {
return false;
}
$product = Mage::getModel('catalog/product')
->setStoreId(Mage::app()->getStore()->getId())
->load($productId);
/* #var $product Mage_Catalog_Model_Product */
if (!$product->getId() || !$product->isVisibleInCatalog() || !$product->isVisibleInSiteVisibility()) {
return false;
}
Mage::register('current_product', $product);
Mage::register('product', $product);
return $product;
}
/**
* Load review model with data by passed id.
* Return false if review was not loaded or review is not approved.
*
* #param int $productId
* #return bool|Mage_Review_Model_Review
*/
protected function _loadReview($reviewId)
{
if (!$reviewId) {
return false;
}
$review = Mage::getModel('review/review')->load($reviewId);
/* #var $review Mage_Review_Model_Review */
if (!$review->getId() || !$review->isApproved() || !$review->isAvailableOnStore(Mage::app()->getStore())) {
return false;
}
Mage::register('current_review', $review);
return $review;
}
/**
* Submit new review action
*
*/
public function postAction()
{
if (!$this->_validateFormKey()) {
// returns to the product item page
$this->_redirectReferer();
return;
}
if ($data = Mage::getSingleton('review/session')->getFormData(true)) {
$rating = array();
if (isset($data['ratings']) && is_array($data['ratings'])) {
$rating = $data['ratings'];
}
} else {
$data = $this->getRequest()->getPost();
$rating = $this->getRequest()->getParam('ratings', array());
}
if (($product = $this->_initProduct()) && !empty($data)) {
$session = Mage::getSingleton('core/session');
/* #var $session Mage_Core_Model_Session */
$review = Mage::getModel('review/review')->setData($this->_cropReviewData($data));
/* #var $review Mage_Review_Model_Review */
$validate = $review->validate();
if ($validate === true) {
try {
$review->setEntityId($review->getEntityIdByCode(Mage_Review_Model_Review::ENTITY_PRODUCT_CODE))
->setEntityPkValue($product->getId())
->setStatusId(Mage_Review_Model_Review::STATUS_PENDING)
->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId())
->setStoreId(Mage::app()->getStore()->getId())
->setStores(array(Mage::app()->getStore()->getId()))
->save();
foreach ($rating as $ratingId => $optionId) {
Mage::getModel('rating/rating')
->setRatingId($ratingId)
->setReviewId($review->getId())
->setCustomerId(Mage::getSingleton('customer/session')->getCustomerId())
->addOptionVote($optionId, $product->getId());
}
$review->aggregate();
$session->addSuccess($this->__('Your review has been accepted for moderation.'));
}
catch (Exception $e) {
$session->setFormData($data);
$session->addError($this->__('Unable to post the review.'));
}
}
else {
$session->setFormData($data);
if (is_array($validate)) {
foreach ($validate as $errorMessage) {
$session->addError($errorMessage);
}
}
else {
$session->addError($this->__('Unable to post the review.'));
}
}
}
if ($redirectUrl = Mage::getSingleton('review/session')->getRedirectUrl(true)) {
$this->_redirectUrl($redirectUrl);
return;
}
$this->_redirectReferer();
}
/**
* Show list of product's reviews
*
*/
public function listAction()
{
if ($product = $this->_initProduct()) {
Mage::register('productId', $product->getId());
$design = Mage::getSingleton('catalog/design');
$settings = $design->getDesignSettings($product);
if ($settings->getCustomDesign()) {
$design->applyCustomDesign($settings->getCustomDesign());
}
$this->_initProductLayout($product);
// update breadcrumbs
if ($breadcrumbsBlock = $this->getLayout()->getBlock('breadcrumbs')) {
$breadcrumbsBlock->addCrumb('product', array(
'label' => $product->getName(),
'link' => $product->getProductUrl(),
'readonly' => true,
));
$breadcrumbsBlock->addCrumb('reviews', array('label' => Mage::helper('review')->__('Product Reviews')));
}
$this->renderLayout();
} elseif (!$this->getResponse()->isRedirect()) {
$this->_forward('noRoute');
}
}
/**
* Show details of one review
*
*/
public function viewAction()
{
$review = $this->_loadReview((int) $this->getRequest()->getParam('id'));
if (!$review) {
$this->_forward('noroute');
return;
}
$product = $this->_loadProduct($review->getEntityPkValue());
if (!$product) {
$this->_forward('noroute');
return;
}
$this->loadLayout();
$this->_initLayoutMessages('review/session');
$this->_initLayoutMessages('catalog/session');
$this->renderLayout();
}
/**
* Load specific layout handles by product type id
*
*/
protected function _initProductLayout($product)
{
$update = $this->getLayout()->getUpdate();
$update->addHandle('default');
$this->addActionLayoutHandles();
$update->addHandle('PRODUCT_TYPE_'.$product->getTypeId());
if ($product->getPageLayout()) {
$this->getLayout()->helper('page/layout')
->applyHandle($product->getPageLayout());
}
$this->loadLayoutUpdates();
if ($product->getPageLayout()) {
$this->getLayout()->helper('page/layout')
->applyTemplate($product->getPageLayout());
}
$update->addUpdate($product->getCustomLayoutUpdate());
$this->generateLayoutXml()->generateLayoutBlocks();
}
/**
* Crops POST values
* #param array $reviewData
* #return array
*/
protected function _cropReviewData(array $reviewData)
{
$croppedValues = array();
$allowedKeys = array_fill_keys(array('detail', 'title', 'nickname','goodthings','badthings'), true);
foreach ($reviewData as $key => $value) {
if (isset($allowedKeys[$key])) {
$croppedValues[$key] = $value;
}
}
return $croppedValues;
}
}
form after submit not saving value of custom fields but default field are saving to database, i have tried to debug with Mage::log(); and i am getting value for all fields in my controller on submit but not getting in Model and no log print in my Model.
I was facing the same issue, but was able to solve it. Below are the steps:
1) my phtml file: added a custom field 'Email'
<li>
<label for="email_field" class="required"><?php echo $this->__('Email Address') ?> :</label>
<div class="input-box">
<input type="text" name="email" id="email_field" value="<?php echo $email; ?>" class="form-control validate-email" placeholder="Enter your Email" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Enter your Email'" />
</div>
</li>
2) Created sql script to add new field to review_detail table
$installer = $this;
$installer->startSetup();
$installer->getConnection()
->addColumn(
$installer->getTable('review/review_detail'),
'customer_email',array(
'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
'nullable' => false,
'length' => 255,
'after' => 'detail',
'comment' => 'Customer Email'
)
);
$installer->endSetup();
3) Override Mage/Review/controllers/ProductController.php and edit method _cropReviewData
protected function _cropReviewData(array $reviewData)
{
$croppedValues = array();
$allowedKeys = array_fill_keys(array('detail', 'title', 'nickname','customer_email'), true);
foreach ($reviewData as $key => $value) {
if (isset($allowedKeys[$key])) {
$croppedValues[$key] = $value;
}
}
return $croppedValues;
}
we have added our custom field customer_email to array
3) Override Mage_Review_Model_Resource_Review.php and edit method _afterSave and add custom field to $detail array
$detail = array(
'title' => $object->getTitle(),
'detail' => $object->getDetail(),
'nickname' => $object->getNickname(),
'customer_email' => $object->getCustomerEmail(),
);
After doing the above changes, the value for custom field will get saved to DB
Please try this and if not resolved let us know code of controller for save reviews
I want to integrate elasticsearch in my laravel project.
I have installed using following line :
Run command on terminal :
composer require shift31/laravel-elasticsearch:~1.0
Then i have created elasticsearch.php in app/config/ and added following code.
<?php
use Monolog\Logger;
return array(
'hosts' => array(
'your.elasticsearch.server:9200' // what should be my host ?
),
'logPath' => 'path/to/your/elasticsearch/log',
'logLevel' => Logger::INFO
);
My first question : What should i write in place of host name
Right now my project is running on local server with localhost:8000.
I have added Shift31\LaravelElasticsearch\ElasticsearchServiceProvider in app/config/app.php for enable the 'Es' facade.
Above all things done. Now in which file i should add the code of elasticsearch to add, update, delete and search the records.
I have product table I need to add product records in elasticsearch, when update product, records should be update.
I have no idea of the further process. Please guide me I have searched on google but no any example help me.
Create the following helper classes in their respective paths:
App\Traits\ElasticSearchEventTrait.php
<?php
Namespace App\Traits;
trait ElasticSearchEventTrait {
public $esRemoveDefault = array('created_at','updated_at','deleted_at');
public static function boot()
{
parent::boot();
static::bootElasticSearchEvent();
}
public static function bootElasticSearchEvent()
{
static::created(function ($model) {
if(isset($model->esEnabled) && $model->esEnabled === true)
{
$model->esCreate();
}
});
static::updated(function ($model) {
if(isset($model->esEnabled) && $model->esEnabled === true)
{
$model->esUpdate();
}
});
static::deleted(function ($model) {
if(isset($model->esEnabled) && $model->esEnabled === true)
{
$model->esUpdate();
}
});
}
private function esCreate()
{
//esContext is false for polymorphic relations with no elasticsearch indexing
if(isset($this->esMain) && $this->esMain === true && $this->esContext !== false)
{
\Queue::push('ElasticSearchHelper#indexTask',array('id'=>$this->esGetId(),'class'=>get_class($this),'context'=>$this->esGetContext(),'info-context'=>$this->esGetInfoContext(),'excludes'=>$this->esGetRemove()));
}
else
{
$this->esUpdate();
}
}
private function esUpdate()
{
//esContext is false for polymorphic relations with no elasticsearch indexing
if($this->esContext !== false)
{
\Queue::push('ElasticSearchHelper#updateTask',array('id'=>$this->esGetId(),'class'=>get_class($this),'context'=>$this->esGetContext(),'info-context'=>$this->esGetInfoContext(),'excludes'=>$this->esGetRemove()));
}
}
/*
* Get Id of Model
*/
public function esGetId()
{
if(isset($this->esId))
{
return $this->esId;
}
else
{
return $this->id;
}
}
public function esGetInfoContext()
{
if(isset($this->esInfoContext))
{
return $this->esInfoContext;
}
else
{
throw new \RuntimeException("esInfoContext attribute or esGetInfoContext() is not set in class '".get_class($this)."'");
}
}
/*
* Name of main context of model
*/
public function esGetContext()
{
if(isset($this->esContext))
{
return $this->esContext;
}
else
{
throw new \RuntimeException("esContext attribute or esGetContext() method must be set in class '".get_class($this)."'");
}
}
/*
* All attributes that needs to be removed from model
*/
public function esGetRemove()
{
if(isset($this->esRemove))
{
return array_unique(array_merge($this->esRemoveDefault,$this->esRemove));
}
else
{
return $this->esRemoveDefault;
}
}
/*
* Extends Illuminate Collection to provide additional array functions
*/
public function newCollection(array $models = Array())
{
return new Core\Collection($models);
}
/**
* Return a timestamp as DateTime object.
*
* #param mixed $value
* #return \Carbon\Carbon
*/
public function asEsDateTime($value)
{
// If this value is an integer, we will assume it is a UNIX timestamp's value
// and format a Carbon object from this timestamp. This allows flexibility
// when defining your date fields as they might be UNIX timestamps here.
if (is_numeric($value))
{
return \Carbon::createFromTimestamp($value);
}
// If the value is in simply year, month, day format, we will instantiate the
// Carbon instances from that format. Again, this provides for simple date
// fields on the database, while still supporting Carbonized conversion.
elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $value))
{
return \Carbon::createFromFormat('Y-m-d', $value)->startOfDay();
}
// Finally, we will just assume this date is in the format used by default on
// the database connection and use that format to create the Carbon object
// that is returned back out to the developers after we convert it here.
elseif ( ! $value instanceof DateTime)
{
$format = $this->getEsDateFormat();
return \Carbon::createFromFormat($format, $value);
}
return \Carbon::instance($value);
}
/**
* Get the format for database stored dates.
*
* #return string
*/
private function getEsDateFormat()
{
return $this->getConnection()->getQueryGrammar()->getDateFormat();
}
/*
* Converts model to a suitable format for ElasticSearch
*/
public function getEsSaveFormat()
{
$obj = clone $this;
//Go through ES Accessors
\ElasticSearchHelper::esAccessor($obj);
$dates = $this->getDates();
//Convert to array, then change Date to appropriate Elasticsearch format.
//Why? Because eloquent's date accessors is playing me.
$dataArray = $obj->attributesToArray();
//Remove all Excludes
foreach($this->esGetRemove() as $ex)
{
if(array_key_exists($ex,$dataArray))
{
unset($dataArray[$ex]);
}
}
if(!empty($dates))
{
foreach($dates as $d)
{
if(isset($dataArray[$d]) && $dataArray[$d] !== "" )
{
//Trigger Eloquent Getter which will provide a Carbon instance
$dataArray[$d] = $this->{$d}->toIso8601String();
}
}
}
return $dataArray;
}
}
App\Services\ElasticServiceHelper.php
<?php
/**
* Description of ElasticSearchHelper: Helps with Indexing/Updating with Elastic Search Server (https://www.elastic.co)
*
* #author kpudaruth
*/
Namespace App\Services;
class ElasticSearchHelper {
/*
* Laravel Queue - Index Task
* #param array $job
* #param array $data
*/
public function indexTask($job,$data)
{
if(\Config::get('website.elasticsearch') === true)
{
if(isset($data['context']))
{
$this->indexEs($data);
}
else
{
\Log::error('ElasticSearchHelper: No context set for the following dataset: '.json_encode($data));
}
}
$job->delete();
}
/*
* Laravel Queue - Update Task
* #param array $job
* #param array $data
*/
public function updateTask($job,$data)
{
if(\Config::get('website.elasticsearch') === true)
{
if(isset($data['context']))
{
$this->updateEs($data);
}
else
{
\Log::error('ElasticSearchHelper: No context set for the following dataset: '.json_encode($data));
}
}
$job->delete();
}
/*
* Index Elastic Search Document
* #param array $data
*/
public function indexEs($data)
{
$params = array();
$params['index'] = \App::environment();
$params['type'] = $data['context'];
$model = new $data['class'];
$form = $model::find($data['id']);
if($form)
{
$params['id'] = $form->id;
if($form->timestamps)
{
$params['timestamp'] = $form->updated_at->toIso8601String();
}
$params['body'][$data['context']] = $this->saveFormat($form);
\Es::index($params);
}
}
/*
* Update Elastic Search
* #param array $data
*/
public function updateEs($data)
{
$params = array();
$params['index'] = \App::environment();
$params['type'] = $data['context'];
$model = new $data['class'];
$form = $model::withTrashed()->find($data['id']);
if(count($form))
{
/*
* Main form is being updated
*/
if($data['info-context'] === $data['context'])
{
$params['id'] = $data['id'];
$params['body']['doc'][$data['info-context']] = $this->saveFormat($form);
}
else
{
//Form is child, we get parent
$parent = $form->esGetParent();
if(count($parent))
{
//Id is always that of parent
$params['id'] = $parent->id;
//fetch all children, given that we cannot save per children basis
$children = $parent->{$data['info-context']}()->get();
if(count($children))
{
//Get data in a format that can be saved by Elastic Search
$params['body']['doc'][$data['info-context']] = $this->saveFormat($children);
}
else
{
//Empty it is
$params['body']['doc'][$data['info-context']] = array();
}
}
else
{
\Log::error("Parent not found for {$data['context']} - {$data['class']}, Id: {$data['id']}");
return false;
}
}
//Check if Parent Exists
try
{
$result = \Es::get([
'id' => $params['id'],
'index' => $params['index'],
'type' => $data['context']
]);
} catch (\Exception $ex) {
if($ex instanceof \Elasticsearch\Common\Exceptions\Missing404Exception || $ex instanceof \Guzzle\Http\Exception\ClientErrorResponseException)
{
//if not, we set it
if (isset($parent) && $parent)
{
$this->indexEs([
'context' => $data['context'],
'class' => get_class($parent),
'id' => $parent->id,
]);
}
else
{
\Log::error('Unexpected error in updating elasticsearch records, parent not set with message: '.$ex->getMessage());
return false;
}
}
else
{
\Log::error('Unexpected error in updating elasticsearch records: '.$ex->getMessage());
return false;
}
}
\Es::update($params);
}
}
/*
* Iterate through all Es accessors of the model.
* #param \Illuminate\Database\Eloquent\Model $object
*/
public function esAccessor(&$object)
{
if(is_object($object))
{
$attributes = $object->getAttributes();
foreach($attributes as $name => $value)
{
$esMutator = 'get' . studly_case($name) . 'EsAttribute';
if (method_exists($object, $esMutator)) {
$object->{$name} = $object->$esMutator($object->{$name});
}
}
}
else
{
throw New \RuntimeException("Expected type object");
}
}
/*
* Iterates over a collection applying the getEsSaveFormat function
* #param mixed $object
*
* #return array
*/
public function saveFormat($object)
{
if($object instanceof \Illuminate\Database\Eloquent\Model)
{
return $object->getEsSaveFormat();
}
else
{
return array_map(function($value)
{
return $value->getEsSaveFormat();
}, $object->all());
}
}
}
A couple of gotchas from the above helper classes:
The default ElasticSearch index is set to the name of the App's Environment
The ..task() functions are meant for the old laravel 4.2 queue format. I've yet to port those to laravel 5.x. Same goes for the Queue::push commands.
Example
ElasticSearch Mapping:
[
'automobile' => [
"dynamic" => "strict",
'properties' => [
'automobile' => [
'properties' => [
'id' => [
'type' => 'long',
'index' => 'not_analyzed'
],
'manufacturer_name' => [
'type' => 'string',
],
'manufactured_on' => [
'type' => 'date'
]
]
],
'car' => [
'properties' => [
'id' => [
'type' => 'long',
'index' => 'not_analyzed'
],
'name' => [
'type' => 'string',
],
'model_id' => [
'type' => 'string'
]
]
],
"car-model" => [
'properties' => [
'id' => [
'type' => 'long',
'index' => 'not_analyzed'
],
'description' => [
'type' => 'string',
],
'name' => [
'type' => 'string'
]
]
]
]
]
]
Top level document is called 'automobile'. Underneath it, you have 'automobile', 'car' & 'car-model'. Consider 'car' & 'car-model' as relations to the automobile. They are known as sub documents on elasticsearch. (See: https://www.elastic.co/guide/en/elasticsearch/guide/current/document.html)
Model: App\Models\Car.php
namespace App\Models;
class Car extends \Eloquent {
use \Illuminate\Database\Eloquent\SoftDeletingTrait;
use \App\Traits\ElasticSearchEventTrait;
protected $table = 'car';
protected $fillable = [
'name',
'serie',
'model_id',
'automobile_id'
];
protected $dates = [
'deleted_at'
];
/* Elastic Search */
//Indexing Enabled
public $esEnabled = true;
//Context for Indexing - Top Level name in the mapping
public $esContext = "automobile";
//Info Context - Secondary level name in the mapping.
public $esInfoContext = "car";
//The following fields will not be saved in elasticsearch.
public $esRemove = ['automobile_id'];
//Fetches parent relation of car, so that we can retrieve its id for saving in the appropriate elasticsearch record
public function esGetParent()
{
return $this->automobile;
}
/*
* Event Observers
*/
public static function boot() {
parent:: boot();
//Attach events to model on start
static::bootElasticSearchEvent();
}
/*
* ElasticSearch Accessor
*
* Sometimes you might wish to format the data before storing it in elasticsearch,
* The accessor name is in the format of: get + attribute's name camel case + EsAttribute
* The $val parameter will always be the value of the attribute that is being accessed.
*
* #param mixed $val
*/
/*
* Elasticsearch Accessor: Model Id
*
* Get the model name and save it
*
* #param int $model_id
* #return string
*/
public function getModelIdEsAttribute($model_id) {
//Fetch model from table
$model = \App\Models\CarModel::find($model_id);
if($model) {
//Return name of model if found
return $model->name;
} else {
return '';
}
}
/*
* Automobile Relationship: Belongs To
*/
public function automobile()
{
return $this->belongsTo('\App\Models\Automobile','automobile_id');
}
}
Example of Search Query:
/**
* Get search results
*
* #param string $search (Search string)
*
*/
public function getAll($search)
{
$params = array();
$params['index'] = App::environment();
//Declare your mapping names in the array which you wish to search on.
$params['type'] = array('automobile');
/*
* Build Query String
*/
//Exact match is favored instead of fuzzy ones
$params['body']['query']['bool']['should'][0]['match']['name']['query'] = $search;
$params['body']['query']['bool']['should'][0]['match']['name']['operator'] = "and";
$params['body']['query']['bool']['should'][0]['match']['name']['boost'] = 2;
$params['body']['query']['bool']['should'][1]['fuzzy_like_this']['like_text'] = $search;
$params['body']['query']['bool']['should'][1]['fuzzy_like_this']['fuzziness'] = 0.5;
$params['body']['query']['bool']['should'][1]['fuzzy_like_this']['prefix_length'] = 2;
$params['body']['query']['bool']['minimum_should_match'] = 1;
//Highlight matches
$params['body']['highlight']['fields']['*'] = new \stdClass();
$params['body']['highlight']['pre_tags'] = array('<b>');
$params['body']['highlight']['post_tags'] = array('</b>');
//Exclude laravel timestamps
$params['body']['_source']['exclude'] = array( "*.created_at","*.updated_at","*.deleted_at");
/*
* Poll search server until we have some results
*/
$from_offset = 0;
$result = array();
//Loop through all the search results
do
{
try
{
$params['body']['from'] = $from_offset;
$params['body']['size'] = 5;
$queryResponse = \Es::search($params);
//Custom function to process the result
//Since we will receive a bunch of arrays, we need to reformat the data and display it properly.
$result = $this->processSearchResult($queryResponse);
$from_offset+= 5;
}
catch (\Exception $e)
{
\Log::error($e->getMessage());
return Response::make("An error occured with the search server.",500);
}
}
while (count($result) === 0 && $queryResponse['hits']['total'] > 0);
echo json_encode($result);
}
/*
* Format search results as necessary
* #param array $queryResponse
*/
private function processSearchResult(array $queryResponse)
{
$result = array();
//Check if we have results in the array
if($queryResponse['hits']['total'] > 0 && $queryResponse['timed_out'] === false)
{
//Loop through each result
foreach($queryResponse['hits']['hits'] as $line)
{
//Elasticsearch will highlight the relevant sections in your query in an array. The below creates a readable format with · as delimiter.
$highlight = "";
if(isset($line['highlight']))
{
foreach($line['highlight'] as $k=>$v)
{
foreach($v as $val)
{
$highlight[] = str_replace("_"," ",implode(" - ",explode(".",$k)))." : ".$val;
}
}
$highlight = implode(" · ",$highlight);
}
//Check the mapping type
switch($line['_type'])
{
case "automobile":
$result[] = array('icon'=>'fa-automobile',
'title'=> 'Automobile',
'id' => $line['_id'],
//name to be displayed on my search result page
'value'=>$line['_source'][$line['_type']]['name']." (Code: ".$line['_id'].")",
//Using a helper to generate the url. Build your own class.
'url'=>\App\Helpers\URLGenerator::generate($line['_type'],$line['_id']),
//And the highlights as formatted above.
'highlight'=>$highlight);
break;
}
}
}
return $result;
}