Magento 2 - change url key programmatically - magento2

Is there a way to generate URL Keys for all products and save them using a script?
I deleted all URL keys for products from database, but now I want to generate them again using a script.
// Edit: I need to do this in Magento 2. Forgot to specify.
I got this until now:
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$obj = $bootstrap->getObjectManager();
$deploymentConfig = $obj->get('Magento\Framework\App\DeploymentConfig');
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$productCollection = $objectManager->create('Magento\Catalog\Model\ResourceModel\Product\CollectionFactory');
$repo = $objectManager->get('Magento\Catalog\Model\ProductRepository');
$collection = $productCollection->create()
->addAttributeToSelect('*')
->load();
foreach ($collection as $product){
$name = $product->getName();
$url = preg_replace('#[^0-9a-z]+#i', '-', $name);
$url = strtolower($url);
echo $url;
$pr = $repo->getById($product->getId());
$pr->setUrlKey($url);
$repo->save($pr);
break;
}
But I get this error:
Fatal error: Call to undefined function Magento\Catalog\Model\Config\Source\Product\Options__() in /home2/magazi70/public_html/vendor/magento/module-catalog/Model/Config/Source/Product/Options/Price.php on line 23

<?php
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$obj = $bootstrap->getObjectManager();
$deploymentConfig = $obj->get('Magento\Framework\App\DeploymentConfig');
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$productCollection = $objectManager->create('\Magento\Catalog\Model\Product');
$collection = $productCollection->create()
->addAttributeToSelect('*')
->load();
foreach ($collection as $product){
$product = $objectManager->create('\Magento\Catalog\Model\Product')->load($product->getId());
$name = $product->getName();
$url = preg_replace('#[^0-9a-z]+#i', '-', $name);
$url = strtolower($url);
$product ->setUrlKey($url);
$product->save($pr);
}

The magento script may take longer time.
1. You can try exporting the products (the csv file will not have url keys)
2. Remove all the attributes and keep only SKU and Name and add a new attribute column url_key
3. Use some Excel Functions to generate url keys using Name
4. Remove the Name column
5. Import the csv

to loading a collection and save the new object of product is slow way to done the job
here is best way to
composer require elgentos/regenerate-catalog-urls
php bin/magento module:enable Iazel_RegenProductUrl
php bin/magento setup:upgrade
more information are available on
https://github.com/elgentos/regenerate-catalog-urls

This code shows how to generate an url key, in a helper class, the same way Magento 2 generates url keys when creating products.
In the example I use dependency injection in order to use Magento\Catalog\Model\Product\Url class in my helper.
namespace Myprojects\Mymodule\Helper;
use Magento\Catalog\Model\Product\Url;
use Magento\Framework\App\Helper\Context as HelperContext;
class Data extends AbstractHelper
{
/**
* #param Url $url
*/
public function __construct(
HelperContext $context,
Url $url
)
{
parent::__construct($context);
$this->url = $url;
}
public function generateUrlKey($string)
{
return $this->url->formatUrlKey($string);
}
}

Related

How to write an extbase Repository-Method for Update in TYPO3?

I have written an update query in TYPO3, Now I need to change it to query-object repository method. How to change the code below?
public function updatePaymentDetails($uid, $txnID, $amt, $stats)
{
$itemUID = $uid;
$transID = $txnID;
$amountPaid = $amt;
$txStatus = $stats;
$tableName = 'tx_paypalpayment_domain_model_transactions AS tpp';
$whereCondition = 'tpp.uid=' . '"' . $itemUID . '"';
$setValues = ['transactionid' => $transID, 'amount' => $amountPaid, 'txnstatus' => $txStatus];
$result = $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tableName, $whereCondition, $setValues);
return $result;
}
I created this much in my own idea (don't know it is correct or not), What should be the remaining portion?
public function paymentUpdate($uid, $txnID, $amt, $stats) {
$query = $this->createQuery();
$query->matching(
$query->logicalAnd(
$query->equals("transactionid", $txnID),
$query->equals("amount", $amt),
$query->equals("txnstatus", $stats)
)
);
/*--- Update Code ---*/
return $query->execute();
}
Is there any way to do that?
The TYPO3/Extbase way is to first fetch your transaction from the persistence layer then apply your changes to the domain object and then update it in your repository.
Something like below in your controller action:
$transaction = $this->transactionRepository->findByIdentifier($itemUid);
$transaction->setTransactionId($transID);
$transaction->setAmount($amountPaid);
$transaction->setStatus($txStatus);
$this->transactionRepository->update($transaction);
If you wants to do a direct update instead of first fetching the record then take a look at the \TYPO3\CMS\Core\Database\Query\QueryBuilder (Only exists in newer versions of TYPO3 - 8.7 and above). In older versions of TYPO3 you could take a look at $GLOBALS['TYPO3_DB']->exec_*.

Prepare content does not work for Sourcerer and DirectPHP

In my Joomla website, I need to execute some custom SQL queries, that have to select different titles from related categories.
Problem I have it works like option Prepare Content is turned off, so all of my content is outside HTML tags.
Module content looks like this:
{source}
<?php
$var_result = '';
$var_categories = array();
$var_category_list = array();
$db =& JFactory::getDBO();
$query = 'select * from jneg_categories where parent_id = 9';
$db->setQuery($query,0,300);
$res = $db->loadObjectList();
foreach ( $res as $row ) {
$var_categories[($row->id)] = $row->title;
$var_category_list[] = $row->id;
}
$var_category_list = implode($var_category_list, ', ');
$sql = "select * from jneg_content where catid IN (".$var_category_list.") order by `catid`";
$db->setQuery($sql,0,30000);
$res = $db->loadObjectList();
$var_current_cat = 0;
foreach ( $res as $row ) {
if ($current_cat != $row->catid) {
$current_cat = $row->catid;
echo '<h2>'.$categories[($row->catid)] . '</h2>';
echo '<br>';
}
echo $row->title;
echo '<br>';
}
?>
{/source}
Can you help me how to get proper HTML as a result of this code please.
Sourcer or other php rendering plugins don't run in html modules unless you go under the module 'options' and select 'prepare content'...
...or you can use this module and just include your php file directly:
https://github.com/idea34/mod_show
Ok, I did it with Jumi plugin - http://2glux.com/projects/jumi/usage-for-j15
Thank you anyway.

ZF2 - Show just one error on forms

I can't seem to get ZF2 to show just one error message for failed form validation messages.
For example, an EmailAddress validator can pass back up to 7 messages and typically shows the following if the user has made a typo:
oli.meffff' is not a valid hostname for the email address
The input appears to be a DNS hostname but cannot match TLD against known list
The input appears to be a local network name but local network names are not allowed
How can I override the error to show something a little more friendly, such as "Please enter a valid email address" instead of specifics like the above?
OK, managed to come up with a solution for this. Instead of using the same string as the error for all validator failures as Sam suggested above, I have overridden the error messages in the InputFilter for the elements and then used a custom form error view helper to show only the first message.
Here is the helper:
<?php
namespace Application\Form\View\Helper;
use Traversable;
use \Zend\Form\ElementInterface;
use \Zend\Form\Exception;
class FormElementSingleErrors extends \Zend\Form\View\Helper\FormElementErrors
{
/**
* Render validation errors for the provided $element
*
* #param ElementInterface $element
* #param array $attributes
* #throws Exception\DomainException
* #return string
*/
public function render(ElementInterface $element, array $attributes = array())
{
$messages = $element->getMessages();
if (empty($messages)) {
return '';
}
if (!is_array($messages) && !$messages instanceof Traversable) {
throw new Exception\DomainException(sprintf(
'%s expects that $element->getMessages() will return an array or Traversable; received "%s"',
__METHOD__,
(is_object($messages) ? get_class($messages) : gettype($messages))
));
}
// We only want a single message
$messages = array(current($messages));
// Prepare attributes for opening tag
$attributes = array_merge($this->attributes, $attributes);
$attributes = $this->createAttributesString($attributes);
if (!empty($attributes)) {
$attributes = ' ' . $attributes;
}
// Flatten message array
$escapeHtml = $this->getEscapeHtmlHelper();
$messagesToPrint = array();
array_walk_recursive($messages, function ($item) use (&$messagesToPrint, $escapeHtml) {
$messagesToPrint[] = $escapeHtml($item);
});
if (empty($messagesToPrint)) {
return '';
}
// Generate markup
$markup = sprintf($this->getMessageOpenFormat(), $attributes);
$markup .= implode($this->getMessageSeparatorString(), $messagesToPrint);
$markup .= $this->getMessageCloseString();
return $markup;
}
}
It's just an extension of FormElementErrors with the render function overridden to include this:
// We only want a single message
$messages = array(current($messages));
I then insert the helper into my application using the solution I posted to my issue here.

Zend session and zend auth

I have made a login system through zend auth here is the code
// userAuthentication
public function authAction(){
$request = $this->getRequest();
$registry = Zend_Registry::getInstance();
$auth = Zend_Auth::getInstance();
$DB = $registry['DB'];
$authAdapter = new Zend_Auth_Adapter_DbTable($DB);
$authAdapter->setTableName('user')
->setIdentityColumn('user_name')
->setCredentialColumn('user_password');
$username = $request->getParam('username');
$password = $request->getParam('password');
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);
$result = $auth->authenticate($authAdapter);
if($result->isValid()){
$data = $authAdapter->getResultRowObject(null,'password');
$auth->getStorage()->write($data);
$this->_redirect('/login/controlpannel');
}else{
$this->_redirect('/login/login');
}
}
This work fine now. There is user_id (column) in user (table) where there are username and password too. I need to get that specific user_id from this table which just login and put it in session through
$user_session = new Zend_Session_Namespace('user_session');
$user_session->username = $username;
$user_id->user_id = $user_id;
so that I can query some info against this $user_id and pass the result into view (name) controlpanel
Get user id from storage :
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
echo $userInfo->user_id;
While this was already answered, I tend to use the getIdentity() function more frequently than the getStorage()->read() chain. Examples below.
// to check if authenticated
Zend_Auth::getInstance()->hasIdentity();
// to actually get the details from storage
Zend_Auth::getInstance()->getIdentity()->user_id;
// if I need to use the identity over and over
$identity = Zend_Auth::getInstance()->getIdentity();
$userId = $identity->user_id;
You can access the data the way Teez suggest or just pull it from Zend_Session_Namespace.
15.1.3.1. Default Persistence in the PHP Session By default, Zend_Auth provides persistent storage of the identity from a successful
authentication attempt using the PHP session. Upon a successful
authentication attempt, Zend_Auth::authenticate() stores the identity
from the authentication result into persistent storage. Unless
configured otherwise, Zend_Auth uses a storage class named
Zend_Auth_Storage_Session, which, in turn, uses Zend_Session. A custom
class may instead be used by providing an object that implements
Zend_Auth_Storage_Interface to Zend_Auth::setStorage().
Zend_Auth_Storage_Session uses a session namespace of 'Zend_Auth'.
This namespace may be overridden by passing a different value to the
constructor of Zend_Auth_Storage_Session, and this value is internally
passed along to the constructor of Zend_Session_Namespace. This should
occur before authentication is attempted, since
Zend_Auth::authenticate() performs the automatic storage of the
identity.
assigning an array to a session, you must provide a name to the session you area creating, i.e. you must do setStorage before you do getStorage.
you must write your code like this:
// userAuthentication
public function authAction(){
$request = $this->getRequest();
$registry = Zend_Registry::getInstance();
$auth = Zend_Auth::getInstance();
$DB = $registry['DB'];
$authAdapter = new Zend_Auth_Adapter_DbTable($DB);
$authAdapter->setTableName('user')
->setIdentityColumn('user_name')
->setCredentialColumn('user_password');
$username = $request->getParam('username');
$password = $request->getParam('password');
$authAdapter->setIdentity($username);
$authAdapter->setCredential($password);
$authAdapter->setStorage(new Zend_Auth_Storage_Session('User_Auth'));
$result = $auth->authenticate($authAdapter);
if($result->isValid()){
$data = $authAdapter->getResultRowObject(null,'password');
$auth->getStorage()->write($data);
$this->_redirect('/login/controlpannel');
}else{
$this->_redirect('/login/login');
}
}
and then to get your storage value, you must use this:
$x = new Zend_Auth_Storage_Session('User_Auth');
$y = $x->read();
and you get everything in $y as an object.
Enjoy!
This is my approach and it s working nice:
1-i start by defining an init function in the bootstrap
protected function _initSession()
{
$UserSession = new Zend_Session_Namespace('UserSession');
$UserSession->setExpirationSeconds(/* you may fix a limit */);
Zend_Registry::set('UserSession', $UserSession);
}
/* in the Login action,after correct username & pwd */
// Create session
$UserSession = Zend_Registry::get('UserSession');
// Get the user from database
$db = Zend_Db_Table::getDefaultAdapter();
$user = $db->fetchRow("SELECT * FROM user_table WHERE user_email = '".$user_email."'");
//then you assign to $user to $UserSession variable :
$UserSession->user = $user;
//finaly don't forget to unset session variable in the Logout action ...
user = Zend_Auth::getInstance()->getIdentity();
if(!#$this->user){
$objSession->errorMsg = " Please Login First .. ! ";
$this->_redirect('/user/login');
}
?>

how to build query string in zend framework?

I'm trying to build a query string as following:
Next Page
I want to add an array to query string. For example, array('find_loc'=>'New+York', 'find_name'=>'starbucks')
I expect to get url that looks like http://example.com/1/?find_loc=New+York&find_name=starbucks
What's the best way to do this? I found a similar question that suggested appending the string to the url. Is there a helper for query string?
Simple answer to your question is no.
Here is the class description:
/**
* Helper for making easy links and getting urls that depend on the routes and router
*
* #package Zend_View
* #subpackage Helper
* #copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* #license http://framework.zend.com/license/new-bsd New BSD License
*/
Helper for making easy links and getting urls that depend on the routes and router
I think the description is clear in it's purpose. Use it for making URLs that depend on the routes and router. So, just append your query strings as recommend in the link you posted in your question.
The following should work for you:
Next Page
The ZF-Router will map the values to the Request object.
In your controller you can access these params with the Response-Object:
$loc = $this->getRequest()->getParam('find_loc');
$name = $this->getRequest()->getParam('find_name);
You can make custom helper:
class My_View_Helper_UrlHttpQuery extends Zend_View_Helper_Abstract
{
public function urlHttpQuery($query)
{
$urlHelper = $this->view->getHelper('url');
$params = func_get_args();
array_shift($params);//removing first argument
$url = call_user_func_array(($urlHelper, 'url'), $params);
if(!is_string($query)) { //allow raw query string
$query = array($query);
$query = http_build_query($query);
}
if(!empty($query) {
$url .= '?' . ltrim('?', $query);
}
return $url;
}
}
After you register this helper with view, you can use it like this Next Page
Working code
/**
* Class Wp_View_Helper_UrlHttpQuery
*/
class Wp_View_Helper_UrlHttpQuery extends Zend_View_Helper_Abstract
{
public function urlHttpQuery($query = array())
{
$urlHelper = $this->view->getHelper('url');
$params = func_get_args();
//removing first argument
array_shift($params);
$url = call_user_func_array(array($urlHelper, 'url'), $params);
if (is_array($query) || is_object($query)) {
$query = http_build_query($query);
}
if (!empty($query)) {
$url .= '?' . ltrim($query, '?');
}
return $url;
}
}
since the upstream code doesn't work