Zend redirector and module as subdomain - zend-framework

I have configured route for cms in my project to cms.m.dev
resources.router.routes.cms.type = "Zend_Controller_Router_Route_Hostname"
resources.router.routes.cms.route = "m.dev"
resources.router.routes.cms.defaults.module = "cms"
resources.router.routes.cms.chains.admin_default.route = "/:controller/:action/*"
resources.router.routes.cms.chains.admin_default.defaults.action = "index"
resources.router.routes.cms.chains.admin_default.defaults.controller = "dashboard"
but now I have problem with redirector:
$this->_redirector->gotoUrl('/doctor/edit/');
$this->_redirector->gotoSimple('edit', 'doctor', 'cms');
When I call it like this it redirects me to to m.dev/cms/doctor/edit instead of cms.m.dev/doctor/edit . Is there any way I can tell redirector that module is subdomain, not subdirectory?
UPDATE: I have created route:
resources.router.routes.cms_doctor_index.route = "/doctor/"
resources.router.routes.cms_doctor_index.type = "Zend_Controller_Router_Route_Static"
resources.router.routes.cms_doctor_index.defaults.module = "cms"
resources.router.routes.cms_doctor_index.defaults.controller = "doctor"
resources.router.routes.cms_doctor_index.defaults.action = "index"
But it still redirects me to m.dev/doctor/

You should try gotoRoute() to specify that you want the redirector to use your special route
gotoRoute (
array $urlOptions = array(),
$name = null, // $name = "m.dev"
$reset = false,
$encode = true
)
I haven't tested this, but it should work.

Related

Prestashop 1.7.8.2 add country in registration form

i need to add a select with defined in admin countries to a prestashop registration form.
Any hint on how to do this?
Simple prestashop 1.7.8.2
Was searching the web, but no strict answers.
You can use additionalCustomerFormFields hook that is placed inside CustomerFormatter class.
Example usage:
https://github.com/PrestaShop/ps_emailsubscription/blob/dev/ps_emailsubscription.php#L1005
Well, i managed to sort it out myself also by:
Adding select with countries that are added to Presta in file /override/classes/form/CustomerFormatter.php
$countries = Country::getCountries((int)$this->language->id, true, false, false);
if (count($countries) > 0) {
$countryField = (new FormField)
->setName('id_country')
->setType('countrySelect')
->setLabel($this->translator->trans('Country', [], 'Shop.Forms.Labels'))
->setRequired(true);
foreach ($countries as $country) {
$countryField->addAvailableValue(
$country['id_country'],
$country['country']
);
}
$format[$countryField->getName()] = $countryField;
}
Adding to file /override/classes/AuthController.php right below:
if ($hookResult && $register_form->submit()) {
this code:
//address saving
$customer = new Customer();
$customer = $customer->getByEmail($register_form->getCustomer()->email);
$address = new Address(
null,
$this->context->language->id
);
$address->id_country = (int) Tools::getCountry();
$address->address1 = Tools::getValue('address1');
$address->postcode = Tools::getValue('postcode');
$address->city = Tools::getValue('city');
$address->phone = Tools::getValue('phone');
$address->firstname = $customer->firstname;
$address->lastname = $customer->lastname;
$address->id_customer = (int) $customer->id;
$address->id_state = 0;
$address->alias = $this->trans('My Address', [], 'Shop.Theme.Checkout');
if($address->save()){
$should_redirect = true;
} else {
$customer->delete();
$this->errors[] = $this->trans('Could not update your information, please check your data.', array(), 'Shop.Notifications.Error');
$this->redirectWithNotifications($this->getCurrentURL());
}
}
The above code is responsible to add new address within provided data. In My case additional is only country, but if You want to add more address data the fields should be added again in CustomerFormatter.php
Credits for several parts of the code:
https://prestapros.com/en/blog/additional-fields-for-registration-form-prestashop-1-7
And:
https://www.prestashop.com/forums/topic/621262-prestashop-17-add-address-in-registration-form/?do=findComment&comment=3380394
Cheers!

How to get Filterable Attributes from a category in Magento 2

I have created category "Bag" in Magento 2. having filter attribute:
color
Size
I'm trying to get Filterable Attributes from category "Bag".
I have already done this in Magento 1.9:
Mage::app()->setCurrentStore($store);
$layer = Mage::getModel("catalog/layer");
$category = Mage::getModel("catalog/category")->load($categoryid);
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
But it does not seem to work for 2.x
I faced the same problem recently.
I documented my investigation here.
I was not able to find framework api to provide filterable attributes for specific category, however I will share workarounds.
Basically all filterable attributes in Magento 2 can be retrived from FilterableAttributeList:
$filterableAttributes = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Category\FilterableAttributeList::class);
$attributes = $filterableAttributes->getList();
Please use DI instead of ObjectManager::getInstance(). I used it just to have more compact example :)
Retrieving filters involved in layered navigation is a bit more tricky.
$filterableAttributes = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Category\FilterableAttributeList::class);
$appState = ObjectManager::getInstance()->get(\Magento\Framework\App\State::class);
$layerResolver = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Resolver::class);
$filterList = ObjectManager::getInstance()->create(
\Magento\Catalog\Model\Layer\FilterList::class,
[
'filterableAttributes' => $filterableAttributes
]
);
$category = 1234;
$appState->setAreaCode('frontend');
$layer = $layerResolver->get();
$layer->setCurrentCategory($category);
$filters = $filterList->getFilters($layer);
However, this is not the final result. To be sure that filters are actual, it is required to check number of items for each filters. (that check is actually performed during core layered navigation rendering)
$finalFilters = [];
foreach ($filters as $filter) {
if ($filter->getItemsCount()) {
$finalFilters[] = $filter;
}
}
Then you can get filter names and values. ie:
$name = $filter->getName();
foreach ($filter->getItems() as $item) {
$value = $item->getValue();
}
Finally, I would like to add alternative solution, that is a bit brutal, thought :)
$categoryId = 1234;
$resource = ObjectManager::getInstance()->get(\Magento\Framework\App\ResourceConnection::class);
$connection = $resource->getConnection();
$select = $connection->select()->from(['ea' => $connection->getTableName('eav_attribute')], 'ea.attribute_id')
->join(['eea' => $connection->getTableName('eav_entity_attribute')], 'ea.attribute_id = eea.attribute_id')
->join(['cea' => $connection->getTableName('catalog_eav_attribute')], 'ea.attribute_id = cea.attribute_id')
->join(['cpe' => $connection->getTableName('catalog_product_entity')], 'eea.attribute_set_id = cpe.attribute_set_id')
->join(['ccp' => $connection->getTableName('catalog_category_product')], 'cpe.entity_id = ccp.product_id')
->where('cea.is_filterable = ?', 1)
->where('ccp.category_id = ?', $categoryId)
->group('ea.attribute_id');
$attributeIds = $connection->fetchCol($select);
Then it is possible to use attribute ids to load collection.
/** #var $collection \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection */
$collection = $this->collectionFactory->create();
$collection->setItemObjectClass('Magento\Catalog\Model\ResourceModel\Eav\Attribute')
->addStoreLabel($this->storeManager->getStore()->getId());
$collection->addFieldToFilter('attribute_id', ['in' => $attributeIds]);
If you know how to build module then you can take help from 'FiltersProvider.php' from 'module-catalog-graph-ql\Model\Resolver\Layer'.
use Magento\Catalog\Model\Layer\Category\FilterableAttributeList as CategoryFilterableAttributeList;
use Magento\Catalog\Model\Layer\FilterListFactory;
use Magento\Catalog\Model\Layer\Resolver;
use Magento\Framework\UrlInterface;
public function __construct(
Resolver $layerResolver,
FilterListFactory $filterListFactory,
CategoryFilterableAttributeList $categoryFilterableAttributeList,
UrlInterface $urlBuilder
) {
$this->_navigation = $navigation;
$this->layerResolver = $layerResolver;
$this->filterListFactory = $filterListFactory;
$this->urlBuilder = $urlBuilder;
$this->_categoryFilterableAttributeList = $categoryFilterableAttributeList;
}
public function getCatMenu($catid)
{
$fill_arr = [];
$filterList = $this->filterListFactory->create(['filterableAttributes' => $this->_categoryFilterableAttributeList]);
$layer = clone $this->layerResolver->get();
$layer->setCurrentCategory($catid);
$filters = $filterList->getFilters($layer);
return $fill_arr;
}

How to use extensions in Zend Framework Routing

I'd like to use an extension like .htm in my URLs. Is there a way to accomplish that?
I've defined the following rule:
frontend.route = '/:standort/:id'
When I use the following
frontend.route = '/:standort/:id.htm'
then the name of the variable is id.htm like in $params["id.htm"].
How can I tell the Zend Framework what to use as variables?
Greetings
//Edit
my full config looks like this now:
frontend.type = 'Zend_Controller_Router_Route_Regex'
frontend.route = '/(.?)/(\w+?\.htm)'
frontend.defaults.module = 'frontend'
frontend.defaults.controller = 'index'
frontend.defaults.action = 'index'
frontend.defaults.standort = 'national'
frontend.map.1 = 'standort'
frontend.map.2 = 'id'
this is how I load the config
$file = APPLICATION_PATH . '/modules/' . $module . '/configs/routing.ini';
if(Zend_Loader::isReadable($file)){
$config = new Zend_Config_Ini($file);
$router = Zend_Controller_Front::getInstance()->getRouter();
$router->addConfig($config);
}
You can do this with regex routes:
frontend.type = "Zend_Controller_Router_Route_Regex"
frontend.route = '/(.?)/(\w+?\.htm)'
frontend.map.1 = "standort"
frontend.map.2 = "id"
but unless you're trying to preserve an existing URL structure, I'd just leave the .htm out - it serves no purpose.

Silverstripe - Generic search form doesn't work on Security/Login page

I have defined a searchform in the Page.php file
function AdvancedSearchForm() {
$searchText = isset($this->Query) ? $this->Query : 'Search';
$searchText2 = isset($this->Subquery) ? $this->Subquery : '0';
$searchText3 = isset($this->documentType) ? $this->documentType : 'All';
$searchSections = DataObject::get('SearchSection');
$ss = array();
foreach ($searchSections as $section) {
$ss[$section->ID] = $section->Name;
}
$fileTypes = DataObject::get('FileType');
$ft = array();
foreach ($fileTypes as $type) {
$ft[$type->ID] = $type->Name;
}
$ft[0] = 'All';
ksort($ft);
$fields = new FieldSet(
new TextField('Query','Keywords', $searchText),
new DropdownField('Subquery', '', $ss, $searchText2),
new DropdownField('documentType', '', $ft, $searchText3)
);
$actions = new FieldSet(
new FormAction('AdvancedSearchResults', 'Search')
);
$form = new Form($this->owner, 'AdvancedSearchForm', $fields, $actions);
$form->setFormMethod('GET');
$form->setTemplate('Includes/SearchForm');
$form->disableSecurityToken();
return $form;
}
and all my pages are extended from Page and search form is in the header so appears on all pages. But when user is on Security/Login page and tries to search for something, he gets the following error message
The action 'AdvancedSearchForm' does not exist in class Security
I assume the reason for this is Security page is not extended from Page. How can I set the search from so that it will work on any page including system pages such as Security login page?
You are getting an error because the form is not being passed to the controller you want (~Page_Controller).
When you define the Form object, you should test $this->owner and see if it's a descendant of Page_Controller. If not, you could use something like this and use $controller as the first variable in your Form object creation.
$sitetree_object = SiteTree::get_by_link('some-page-url');
$controller = ModelAsController::controller_for($sitetree_object);

Module based application.ini in Zend Framework

I want to have module based application.ini in my application.
Is it possible?
Basic requirement arises because I am having multiple databases depending on modules.
Please guide.
You can use multiple Db in application.ini:
resources.db.master.adapter = "PDO_MYSQL"
resources.db.master.default = false
resources.db.master.params.dbname = "db1"
resources.db.master.params.host = "127.0.0.1"
resources.db.master.params.username = "root"
resources.db.master.params.password = ""
resources.db.slave.adapter = "PDO_MYSQL"
resources.db.slave.default = true
resources.db.slave.params.dbname = "db2"
resources.db.slave.params.host = "127.0.0.1"
resources.db.slave.params.username = "root"
resources.db.slave.params.password = ""
And initialize in your bootstrap:
public function _initDatabase() {
$config = $this->getApplication()->getOption('resources');
$dbArrays = array();
foreach($config['db'] as $name => $dbConf){
// Set up database
$db = Zend_Db::factory($dbConf['adapter'], $dbConf['params']);
$db->query("SET NAMES 'utf8'");
$dbArrays[$name] = $db;
if((boolean)$dbConf['default']){
Zend_Db_Table::setDefaultAdapter($db);
}
unset($db);
}
Zend_Registry::set("db", $dbArrays);
}
In my case I always save each adapter in the registry so I can use them separately later.
I also made a new class which extend Zend_Db_table where I have My own getAdapter($name) like so:
public function getAdapter($name = null){
if($name !== null){
$dbAdapters = Zend_Registry::get('db');
return $dbAdapters[$name];
}
return parent::getAdapter();
}
With that in each model I can do:
$this->getAdapter('slave')->fecthAll($sql);
$this->getAdapter('master')->fecthAll($sql);
The application.ini is read long before the module is determined. I'd suggest you forget about application.ini and instead try and write a controller plugin that will load in some additional configuration depending on which module was selected.