Supposed there is a function in a \AW\Blog\Model\post.php.there is a function in it.
public function PreNext($type){
$id = $this->_data['post_id'];
$blog = Mage::getResourceModel('blog/post_collection');
$blog->getSelect()->where('post_id>?',$id);
return $blog->getFirstItem();
}
why it write $this->_data['post_id']; could i write it with another.
what are the four lines meaning which in the function within magento.? many thanks.
the post.php
class AW_Blog_Model_Post extends Mage_Core_Model_Abstract{
const NOROUTE_PAGE_ID = 'no-route';
protected function _construct(){
$this->_init('blog/post');
}
public function load($id, $field=null){
return $post = parent::load($id, $field);
}
public function noRoutePage(){
$this->setData($this->load(self::NOROUTE_PAGE_ID, $this->getIdFieldName()));
return $this;
}
public function getShortContent(){
$content = $this->getData('short_content');
if(Mage::getStoreConfig(AW_Blog_Helper_Config::XML_BLOG_PARSE_CMS)){
$processor = Mage::getModel('core/email_template_filter');
$content = $processor->filter($content);
}
return $content;
}
public function getPostContent(){
$content = $this->getData('post_content');
if(Mage::getStoreConfig(AW_Blog_Helper_Config::XML_BLOG_PARSE_CMS)){
$processor = Mage::getModel('core/email_template_filter');
$content = $processor->filter($content);
}
return $content;
}
public function loadByIdentifier($v) {
return $this->load($v, 'identifier');
}
}
This is code of a custom extension, so only people having this extension can know, what this post.php file contains, and whether you can get the value using other ways than $this->_data['post_id'].
If the extension uses standard Magento Getters/Setters, maybe $this->getPostId() may work, too.
The rest loads a collection of records having a post_id greater than $this->_data['post_id'], but returns only the first record found.
Update
The class you posted extends
Mage_Core_Model_Abstract
which in turn extends
Varien_Object
in a standard Magento OOB.
The Varien_Object class defines the standard getters/setters I was talking about, so yes, you can also use $this->getPostId() to get the value.
To understand how these getters/setters work, I'd recommend to check the Varien_Object and read about PHPs magic methods, like __call(), __get() and __set().
Related
Got a stucking situation which produces unnecessary IDE warnings and may lead to cleaning-up used code.
I think the best solution is to add some PHPDoc at the right place, but couldn't find the right place yet because of some constraints, as explained in the below examples.
IDE: PhpStorm
Result:
<?php
/*
* Generic result class, widely used, so can't touch it ¯\_(ツ)_/¯
*/
class Result {
public $data;
public function __construct() {
return $this;
}
public function setData($data) {
$this->data = $data;
return $this;
}
}
Customer:
<?php
class Customer {
public string $name = '';
public string $email = '';
public function __construct() {
$this->name = 'John Smith';
$this->email = 'test#example.com';
}
public function getCustomer(): Result {
return (new Result())->setData(new self());
}
public function reverseName(): string { // ❌ Unused element: 'reverseName'
$parts = explode(' ', $this->name);
return implode(' ', array_reverse($parts));
}
}
Controller:
<?php
require_once 'vendor/autoload.php';
$john = (new Customer())->getCustomer();
// ℹ️ $john->data is an instance of the Customer class.
// How should I tell to the IDE this ^ ❓
$reversedName = $john->data->reverseName(); // ❌ Cannot find declaration to go when trying to navigate to the method
exit($reversedName);
Tried many and many options, but the only one which works is by adding a PHPDoc to Result's $data property. Can't touch it because it's widely used in the project...
LE:: The Customer class has a lot of methods similar to reverseName(), so assigning the data property to a new variable is also difficult to write: /** #var Customer $john */ $john = (new Customer())->getCustomer()->data;
You could try to use another PhpStorm feature called advanced metadata.
Firstly you need to configure metadata in the next way
namespace PHPSTORM_META {
override(\App\Result::getData(0), map(['' => '#']));
}
then add getter to your Result class
public function getData()
{
return $this->data;
}
and use this getter in the next way
$john->getData(Customer::class)->reverseName();
The main idea is to use class name as an argument of the getter method and PhpStorm will know what class object this getter will return.
class KD_DB extends PDO {
protected static $dbOne = '';
protected static $dbTwo = '';
public function setVariable ($alias,$content){
switch($alias){
case'one': self::$dbOne = $content; break;
case'two': self::$dbTwo = $content; break;
}
}
}
Is there a way to create these dynamically?
Something like this to create the protected static variables
public function setVariable ($alias,$content){
self::${$alias} = $content;
}
It did not work, but I suspect it is because I need it to be static to make it to work with a third class that extends this one...
If you only have the two variables, it may be easier (with more appropriate names) to set them using a static function for each one, something like...
class KD_DB {
public static $dbOne = '';
public static $dbTwo = '';
public static function setOne ($content){
self::$dbOne = $content;
}
}
KD_DB::setOne("value for one");
var_dump(KD_DB::$dbOne);
(code with minor changes to show the process)
But if you wanted a more open ended method, I would go for an associative array as the static variables and then use the 1 method (like you currently are) to set the value in the array...
class KD_DB {
public static $data = [];
public static function setVariable ($alias,$content){
self::$data[$alias] = $content;
}
}
KD_DB::setVariable("three", "value for three");
var_dump(KD_DB::$data);
this method can have issues if you mistype a variable reference which should be found during testing though, but does offer flexibility.
I am creating a web site that essentially sells advertising 'spots'. I.e someone can signup and buy a banner advert to be displayed on the home page, or they can buy an advert where they get their own profile page. My point being, although all adverts share common functionality, they do differ.
To accomplish this, my domain model looks like this: (simplified)
class Advert {
protected
$uID,
$startTime,
$traits = array();
public function __construct($_traits) {
$this->traits = $_traits;
}
public function getUID() { return $this->startTime; }
public function getStartTime() { return $this->startTime; }
public function setStartTime($_startTime) { $this->startTime = $_startTime; }
public function save() {
MySQLQuery 'UPDATE adverts SET startTime = $this->startTime WHERE uID = $this->uID';
foreach($this->traits as $trait) {
$trait->save($this->uID);
}
}
....
}
-
interface IAdvertTrait {
public function save($_advertUID);
}
-
class AdvertTraitProfile implements IAdvertTrait {
protected $url;
public function getURL() { return $this->url; }
public function setURL($_url) { $this->url = $_url; }
public function save($_advertUID) {
MySQLQuery 'UPDATE advertdata_profile SET url = $this->url WHERE advertUID = $_advertUID';
}
....
}
-
class AdvertTraitImage implements IAdvertTrait {
protected $image;
public function getImage() { return $this->image; }
public function setImage($_image) { $this->image = $_image; }
public function save($_advertUID) {
MySQLQuery 'UPDATE advertdata_image SET image = $this->image WHERE advertUID = $_advertUID';
}
....
}
There are actually several 'AdvertTrait...' classes, all of which implement IAdvertTrait.
As you can see, if I create an advert like this:
$advert = new Advert(
array(
new AdvertTraitProfile(),
new AdvertTraitImage()
...
)
);
I can then do this:
$advert->save();
And all the required information will get saved to the DB by the Advert itself and each of its AdvertTraits.
Using this method I'm able to create different kinds of advert simply by passing in different 'traits'. However, to my problem - I've no idea how I should go about manipulating an Advert. As per the example above, there is really no point creating and advert and then immediately saving it.
I'd like to be able to this:
$advert->getStartTime(); # Works
$advert->getURL(); # Doesn't work of course, as the getURL method is encapsulated within a property of the Advert's 'traits' array
$advert->setImage('blah.jpg'); # Also does not work
I'm not sure how to go about making these 'internal' methods accessible.
I could just create a different 'Advert' class for each kind of advert i.e:
AdvertProfile extends Advert {
$this->traitProfile = new AdvertTraitProfile();
public function getURL() { return $this->traitProfile->getURL(); }
...
}
AdvertImage extends Advert {
$this->traitImage = new AdvertTraitImage();
public function getImage() { return $this->traitImage->getImage(); }
...
}
AdvertProfileImage extends Advert {
$this->traitProfile = new AdvertTraitProfile();
$this->traitImage = new AdvertTraitImage();
public function getURL() { return $this->traitProfile->getURL(); }
public function getImage() { return $this->traitImage->getImage(); }
...
}
But I feel this is going to get messy; I'd need to keep creating new 'Advert' classes for every combination of traits I need and each advert class would need to define its trait methods in itself so they can be called from an instance of the advert.
I've also messed with the decorator pattern; so instead of passing these 'trait' classes to the constructor of the Advert, I chain the decorators together like:
$advert = new AdvertImageDecorator(new AdvertProfileDecorator(new Advert()));
However this requires the decorators to be able to 'lookup' methods that don't belong to them using method_exists and call_user_func_array which just seems like a big old hack to me. Plus chaining a multitude of decorators together like that just grates on me.
I've also had a look at proper PHP Traits, but IMVHO I do not think they'll help me. For example, every AdvertTrait has a 'save' method, all of which need to be called at the same time. I believe a proper Trait would require me to pick just one 'save' method from one trait.
Maybe I should use plain old inheritance - but then I'd still be creating specific types of Advert, all of which ultimately inherit from Advert. However I believe this would cause further issues; i.e I would not be able to make a AdvertWithProfileAndImageTraits extend from both AdvertWithProfileTraits AND AdvertWithImageTraits.
Can anyone offer a proper solution to this conundrum? Perhaps there is another design pattern I should be using.
Thanks very much,
Dave
I would go for the Decorator approach.
An abstract AdvertDecorator class can look like this:
abstract class AdvertDecorator implements IAdvertTrait {
protected $child;
public function __construct($child=null) {
if(!$child) {
$child = new NullAdvert();
}
$this->child = $child;
}
/**
* With this function all calls to non existing methods gets catched
* and called on the child
*/
public function __call($name, $args) {
return call_user_func_array(array($this->child, $name), $args);
}
}
/**
* This class is for convenience so that every decorator
* don't have to check if there is a child
*/
class NullAdvert implements IAdvertTrait {
public function save($_advertUID) {
// do nothing
}
}
Instead of the NullAdvert class you can use a BaseAdvert class, which implements all of your basic advert logic (like you have done in the Advert class).
Now all other classes extend from this AdvertDecorator class:
class AdvertProfile extends AdvertDecorator {
public function getProfileURL() { ... }
public function save($_advertUID) {
// save own advert
MySQLQuery 'UPDATE advertdata_profile SET url = $this->url WHERE advertUID = $_advertUID';
// save advert of child
$this->child->save($_advertUID);
}
}
class AdvertImage extends AdvertDecorator {
public function getImage() { ... }
public function save($_advertUID) {
// save own advert
MySQLQuery 'UPDATE advertdata_image SET image = $this->image WHERE advertUID = $_advertUID';
// save advert of child
$this->child->save($_advertUID);
}
}
class AdvertProfileImage extends AdvertDecorator {
public function getProfileImageURL() { ... }
public function getProfileImage() { ... }
public function save($_advertUID) {
// save own advert ...
// save advert of child
$this->child->save($_advertUID);
}
}
You can use it like this:
$advert = new AdvertProfile();
$advert = new AdvertImage($advert);
$advert = new AdvertProfileImage($advert);
// save all advert components
$advert->save('uid');
// call functions
$advert->getProfileURL();
$advert->getImage();
$advert->getProfileImageURL();
$advert->getProfileImage();
This structure is IMHO very flexible. Every Advert Component can be added to the current Advert in arbitrary order. Futhermore you can extend this solution with the composite pattern and add a AdvertComposite so that you can group your components. You can even add multiple Advert Components of the same kind to one Advert (for this you have to change the methods a little bit).
OK. here is what I'm trying to do:
class Image{
public $_image;
public $_extension;
public $_mime;
public $_size;
public $_location;
public $_description;
public function __construct($image, $location){
$this->_image = $image;
$this->_location = $location;
$this->_extension = getExtension();
$this->_mime = getMime();
$this->_size = getSize();
}
private functions fallow.....
}
But I keep getting an internal server error when I try to run it. When I comment out the method calls it works. So the question is can I call methods from inside the constructor or am I doing something wrong with the methods.
Do your functions getExtension, getMime and getSize exist? Are they methods on this class? If they are methods, they need to be called with $this->... as in
$this->_extension = $this->getExtension();
If they are not methods, and are functions, you need to make sure the files that contain/define them are loaded before you run the constructor.
Well ..this fragment of code will work as expected:
class Foo
{
protected $secret = null;
public function __construct( $data )
{
$this->secret = $this->makeSecret($data);
}
public function makeSecret( $data )
{
return md5( $data );
}
}
$bar = new Foo( 'lorem ipsum' );
That is not a problem.
But you should know, that is considered to be a bad practice - to do computation/work in the constructor. It makes that class practically untestable. Instead, if you need to perform some computation before "releasing" the object to the rest of the code, you should use a factory. Something along the lines of :
class ImageFactory
{
public function build($image, $location)
{
$instance = new Image($image, $location);
$instance->prepare();
return $instance;
}
}
The class would need some changes:
class Image
{
protected $_image; // you were leaking abstraction
protected $_extension;
protected $_mime;
protected $_size;
protected $_location;
protected $_description;
public function __construct($image, $location)
{
$this->_image = $image;
$this->_location = $location;
}
public function prepare()
{
$this->_extension = $this->getExtension();
$this->_mime = $this->getMime();
$this->_size = $this->getSize();
}
private functions fallow.....
}
Now when you need to create new object you do:
$factory = new ImageFactory;
$image = $factory->build( $file, '/uploads/' );
Of course the instance of ImageFactory can be reusable, and if all your images use the same $location, then you would pass that variable to factory at the initialization. And the factory would be able to "remember it" and pass to all the images it creates:
$factory = new ImageFactory('/uploads/');
$img1 = $factory->build( $file );
$img2 = $factory->build( $something_else );
This is actually how one should deal with creating multiple objects, which all need access to same DB connection instance.
Yes, you can call methods from within the constructor. Remember that the __construct() magic method was implemented in PHP 5. Prior to that, you created a function named the same as your class which acted as your constructor so depending on your PHP version, that could be a problem.
Additionally, the function calls you are making, are they in the class or external? If they are inside the class you need to call them this way:
$this->_extension = $this->getExtension();
You didnt specified what error you are expiriencing clearly. But try calling you class methods even inside the class using this keyword, otherwise it would not work:
public function __construct($image, $location)
{
$this->_image = $image;
$this->_location = $location;
$this->_extension = $this->getExtension();
$this->_mime = $this->getMime();
$this->_size = $this->getSize();
}
Would be a better idea to post your code for the methods you wrote. There could be something wrong within them as well. Possibly forgetting to return a result or something...
I'm trying to decide the design of a system which is meant to allow for a high amount of extensiblity. From what I can tell, a pattern such as the abstract factory would not allow for overriding of the base methods, apart from duplicating code (as demonstrated below).
I've done some preliminary research into aspect oriented programming and it seems to be along the lines of what I'm looking for but I'm having a difficult time wrapping my head around the specifics.
abstract class Object {
protected $object_id;
protected $name;
function LoadObjectData()
{
$file_contents = readfile('object'.$object_id.'.data');
$data = array();
// parse file contents into $data array...
return $data;
}
function Create()
{
$data = $this->LoadObjectData();
$name = $data['name'];
return $data;
}
}
class User extends Object {
protected $email_address;
function Create()
{
$data = parent::Create();
$this->email_address = $data['email_address'];
return $data;
}
}
//----------Module 1-MySQL Lookup-------------
/*
* Redefine Object::LoadObjectData() as follows:
*/
function LoadObjectData()
{
$data = array();
$result = mysql_query("SELECT...");
// construct array from result set
return $data;
}
//----------Module 2-Cache Machine-------------
/*
* Redefine Object::LoadObjectData() as follows:
*/
function LoadObjectData()
{
if (exists_in_cache($object_id)) {
return get_cached_object($object_id);
}
$data = parent::LoadObjectData();
cache_object($object_id, $data);
return $data;
}
(This is sort of a poor example, but hopefully it helps to get my point across)
The intended system would have a very large proportion of methods available to be extended and I would like to minimize the extra effort and learning necessary for developers.
Is AOP exactly what I'm looking for, or is there a better way to deal with this?
Thanks!
So, you want to use a decorator pattern without defining the decorator itself.
If yes, then it's a monkeypatching and can be done with aspect-oriented tools. This can be solved easily with following extensions and frameworks:
PHP Runkit Extension
Go! Aspect-Oriented framework for PHP
PHP-AOP Extension.
You don't have to declare the base class as an abstract class. You can make it a regular class and have it load and instantiate other classes based on passed construct parameters. The constructor can return an instance of a class, not just the class the constructor is in. To avoid duplicating code, you can mix static with instantiated functions and variables. Just remember that a static function or variable is the same for ALL instances. Change a static variable in one and it is changed for all instances. A rather basic example of a plugin architecture.
class BaseObject {
protected static $cache = array();
public function __construct($load_plugin) {
require_once($load_plugin.'.class.php');
$object = new $load_plugin();
return $object;
}
public static function cacheData($cache_key, $data) {
self::$cache[$cache_key] = $data;
}
}
class Plugin extends BaseObject {
public function __construct() {
}
public function loadData() {
// Check the cache first
if ( !isset(self::$cache[$cache_key]) ) {
// Load the data into cache
$data = 'data to cache';
self::cacheData($cache_key, $data);
}
return self::$cache[$cache_key];
}
}