Create new category instance inside template - prestashop 1.7 - prestashop

I know i can use category class methods inside .tpl template files like this:
{assign var='all_categories' value=Category::getCategories()}
But how can i actually initialize Category object inside template? So that __construct function runs.
I ask this because when i try to use some Category class functions, i get this error:
Using $this when not in object context

There is no way to instance a category through a tpl file, some classes have a public static method to do this, eg, like the Db class, this have one called getInstance, unfortunately by default doesn't exist nothing similar in the Category class. You should instance it in a php file and send to Smarty, or modify the class adding an object:
public static $instance = array();
And the method:
public static function getInstance($id_category)
{
if (isset(self::$instance[$id_category])) {
return self::$instance[$id_category];
}
return self::$instance[$id_category] = new Category($id_category);
}
Now you can use in your tpl:
{assign var='category' value=Category::getInstance(3)}

Related

Correct way to extend Prestashop 1.7

In PS 1.7 documentation, we can read multiple time that the PS override system is not recommended, an it's not allowed to publish module in the PS marketplace.
We have to only use hooks, and extend existing class, and I understood why.
But how to use the extended class instead of the core one in our custom theme ?
Let use a example :
I want to add a custom field for categories.
In a module I extend the CategoryCore class :
class Category extend CategoryCore{
private $bottom_description
...
}
Then to add the field in the category's admin page I can use some hook like displayBackOfficeCategory and actionBeforeAddCategory.
But I'm not sure for the front : the new variable have to be accessible in some theme templates files.
In my custom theme, in the category.tpl template, $category->bottom_description is undefined.
Fix this issue by overriding the CategoryController is easy,but how to do this only with hook ?
The only way I found is to use the actionFrontControllerSetMedia hook, like this :
function HookActionFrontControllerSetMedia(){
// get my custom Category object base on url
this->context->smarty->assign(["category_bottom_description"=>$category->buttom_description]);
}
This look tricky, and my new field is still not accessible in other context.
So what is the proper way to get this property available in my custom them ?
Ideally, the new property should be available every time we found a category object, like this : $category->bottom_description.
Do not forget to define a new field at category class because at the front you have not a class instance but an array which is converted within ObjectPresenter class. So follow this way. Extend your Category class and add all necessary definitions
class Category extends CategoryCore
{
public $bottom_description;
public function __construct($idCategory = null, $idLang = null, $idShop = null)
{
Category::$definition['fields']['bottom_description'] = array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml');
$this->bottom_description = 'bottom_description'; // just to add demo data
parent::__construct($idCategory, $idLang, $idShop);
}
}
and then the field bottom_description will be available in the category.tpl but like an array {$category.bottom_description}. Hope it will help you.

Pass parameter to Partial View in ASP.NET Core

On an ASP.NET Core 2.0 application I need to render a partial view and pass a few parameters:
#Html.Partial("Form", new { File = "file.pdf" })
On the partial view I tried to access it using:
#Model.File
And I get the error:
RuntimeBinderException: 'object' does not contain a definition for 'File'
If I simply use on my partial:
#Model
I get the following printed on the page:
{ File = file.pdf }
So there the model is being passed and there is a property File in it.
So what am I missing?
You are passing untyped (anonymous type) data to partial view. You cannot use #Model.File. Instead, you will need to use ViewData's Eval method to retrieve the value.
#ViewData.Eval("File")
Traditional approach is to create a strongly typed ViewModel class, and pass it to the partial view. Then you can access it as #Model.File.
public class SampleViewModel
{
public string File { get; set; }
}
#Html.Partial("Form", new SampleViewModel { File = "file.pdf" })
Inside Partial View,
#model SampleViewModel
<h1>#Model.File</h1>
You should have dynamic as the model of your partial view, this way, you can pass everything - like your anonymous object - and it will just work. Add:
#model dynamic
To the Form.cshtml file.
When you do new { File = "file.pdf" }, you are passing an object that contains an attribute file. Since this is of type object, you can't access any of its variables in c# directly. There some ugly codes to access a fields from an object such as the one that can be found here: C# .NET CORE how to get the value of a custom attribute?
However in this case the most recommended way (for safety) is the create a class and pass an object of that class.
So if you create the following class:
public class MyFileInfo{
public string File { get; set }
}
Then you can create your object by passing:
#Html.Partial("Form", new MyFileInfo{ File = "file.pdf" })
In your partial view, at the beginning, define your model class
#model MyFileInfo
Then in the same file you will now be able to access
#Model.File
For the sake of completeness: you can pass arbitrary variable via a ViewDictionary
#Html.Partial("_Partial", model, new ViewDataDictionary(ViewData) { { "MyVarName", someValue } })
And then access it like this in the partial:
ViewData["MyVarName"]
Another option, you can simply set a ViewData var before calling the partial, and it will be passed on to it
#{
ViewData["MyVarName"] = "hello";
}
#Html.Partial("_Partial", model)
However, strongly typed models are much easier to work with.

TYPO3 extbase: get some from FrontendUserGroupRepository

In a class file I can get all records from another repository that is not mine
$allUsergroups = $this->feGroupRepository->findAll();
How to make custom function to acomplish something like this on such a repository in the most correct way?
// magic default function that takes a uid list (or array) as argument
$someUsergroups = $this->feGroupRepository->findSomeByUidList('2,4,6,8');
Or can I extent an existing repository with my own custom functions, in this case based on $query->in(list)?
You can create your own method in your extensionRepository.php class
you can use :
in($propertyName, $operand)
or
contains($propertyName, $operand)
Contrarily, the methods in() and contains() accept multi-value data types as arguments (e.g. Array, ObjectStorage).
take a look how some other extension are doing stuff. (like the tx_news extension)
or read some docs here :
https://docs.typo3.org/typo3cms/ExtbaseFluidBook/6-Persistence/3-implement-individual-database-queries.html
Yes, you can extend another class in TYPO3 without any need to change any other code. It´s called Dependency Injection in ExtBase context.
First, create a new repository class your_ext/Classes/Domain/Repository/FrontendUserRepository.php and add below content to it:
<?php
namespace Tillebeck\YourExt\Domain\Repository;
class FrontendUserRepository extends \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository {
/**
* #param array $uidList
* #return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
public function findByUidList(Array $uidList)
{
$query = $this->createQuery();
//$query->getQuerySettings()->setRespectStoragePage(false);
$query->matching(
$query->in('uid', $uidList)
);
return $query->execute();
}
/**
* #return string
*/
protected function getRepositoryClassName()
{
return get_parent_class($this);
}
}
Here we have implemented your method findByUidList with the required argument $uidList which needs to be an array.
Because repositories resolve their model names by their own class name, we need to change the method getRepositoryClassName to return the parent class name, in this case TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository.
But this alone won't work. We need to tell ExtBase that every time we inject or initialize a TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository, either by PHPDocBlock annotation #inject or by the objectManager->get, then we really want to initialize our new repository. This is done in TypoScript.
config.tx_extbase.objects {
TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository {
className = Tillebeck\YourExt\Domain\Repository\FrontendUserRepository
}
}
You can also restrict your change to your own extension alone by replacing config.tx_extbase with plugin.tx_yourext.
Last step: clear ALL cache, and possibly delete all files in typo3temp directory.
Now in your controller (or other class) you can run below code.
$uidList = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', '2,4,6,8', true);
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump(
$this->frontendUserRepository->findByUidList($uidList)
);
I have tested above solution in TYPO3 7.6 and it works. Dependency Injection has existed since version 6.1.
This is by my definition the must correct way, as you asked, to implement this feature in your own TYPO3 extension.

Base class for common YII functions?

I know how to create a class the will allow me to instantiate it and use across my project. What I want to be able to do is have functions without instantiating classes. For example, I know how to do this:
$core = new core();
$val = $core->convertToMyNotation($anotherval);
But what I want is to be able to do this ANYWHERE in any view, class whatever:
$val = convertToMyNotation($anotherval);
Where would I place these functions in order to be able to do that?
best way to do it, create a public function in components/Controller.php
public function globalFunction(){
// do something here.
}
and access it anywhere by
$this->globalFunction();
You can define a static method as an option.
class core{
public static function convertToMyNotation($value){
//do whatever here
return $value;
}
}
Then call it like so:
$val = core::convertToMyNotation($anotherval);
This requires no instantiation of the object to use. The only restriction is that you cannot use the $this property inside a static method.
Alternately, just define a file with your functions in it and include the file at some point early like, like within the boostrap script in your public_html/index.php file.
Edit: darkheir makes some good suggestions. Include such a class in your protected/components folder, and have it extend CComponent to gain some potentially useful enhancements.
By including the class in the protected/components folder, you gain the advantage of autoloading the class, by default.
There is no definitive question of your answer, it depends a lot on what the function will be doing!
If the function is performing some things specific to a model
(getting the last users, ...) this has to be in the User model as
Willem Renzema described:
class theModelClass {
public static function convertToMyNotation($value){
//do whatever here
return $value;
}
}
And you'll call it like
$val = theModelClass::convertToMyNotation($anotherval);
If the function is handling user inputs (sanitizing he inputs,
checking the values, ...) then it has to go to the controller and
you'll use Hemc solution:
Create a public function in components/Controller.php
public function globalFunction(){
// do something here.
}
and access it anywhere by
$this->globalFunction();
If the function is an Helper: performing some actions that do not
depend on models or user inoput then you can create a new class that
you'll put in your component directory:
class core extends CComponent{
public static function convertToMyNotation($value){
//do whatever here
return $value;
}
}
And
$val = core::convertToMyNotation($anotherval);
Actually, I think you're looking for this answer instead:
http://www.yiiframework.com/wiki/31/use-shortcut-functions-to-reduce-typing/
In essence, in your entry script, before you load up Yii, include a global functions file:
require('path/to/globals.php');
Then, any function defined in that file can be used as a shortcut. Be careful, but enjoy the power! :-)
Create something like
Class Core extends CApplicationComponent{
public function doSomething(){}
}
and in config main.php
'components'=>array(
'core'=>array(
'class' => 'Core'
),
),
and now you can call whenever you want
Yii::app()->core->doSomething();

Declare global variable in action script?

I want to create a variable(possible global variable) in one action script file and want to use the same variable across all other action script files in the project. How to create such a variable and how to use the same variable across all .as files??
One simple way is to define a static variable in a Class (either create a new Class or use one of your existing classes):
// in MyConfig.as
class MyConfig {
static var myVariable:String = "Hi";
}
// You can access / set the value from any class using MyConfig.myVariable
trace(MyConfig.myVariable); // prints Hi
MyConfig.myVariable = "Hello";
trace(MyConfig.myVariable); // prints Hello
Create one public class (lets assume GlobalVariables.as)
No need to add its instance.
Now declare all the variables that you want to use across multiple classes as STATIC.
(you can also declare and create instances of classes in that class to avoid multiple instances of classes)
Also you can add common methods to this class
So whenever you want to access that variable declared in GlobalVariables class
you need to access using reference e.g GlobalVariables.variableName
sample code:
package classes{
public class GlobalVariables{
public static var strURL:String;
public static function setExternalLinks(){
strURL = "http://demourl.asmx/";
}
}
}