Given following code:
class Picture {
public function getAbsolutePathAttribute() {
return "absolute_path"
}
}
$picture = new Picture();
echo $picture->absolute_path; // prints "absolute_path"
$picture->absolute_path = "I will override you no matter what";
echo $picture->absolute_path; // prints "absolute_path"
Is there way of overriding an eloquent mutator attribute?
I have tried a magic method:
setAbsolutePathAttribute($value) {
$this["attributes"] = $value;
}
However it did not work.
So I don't recommend trying to solve this by overriding the mutator. You might be able to, but the less you touch the core of Laravel the better. Why? Because you never know what future Laravel code could look like.
A core change could possibly break your internal override the next time you do a minor or major Laravel version upgrade.
So, 2 ways to solve this, given your simple example:
1. Create a default value at the database level
Laravel migrations feature a default column modifier, it looks something like this: ->default('something')
That way you don't need the mutator at all for getting that default value you're looking for.
You can read more about them here: https://laravel.com/docs/5.6/migrations#column-modifiers
2. Make your accessor smarter
Your example is pretty simple, so I'll just work with it:
class Picture {
public function getAbsolutePathAttribute() {
if(is_null($this->absolute_path)) {
return "absolute_path";
}
return $this->absolute_path;
}
}
That way it only does something if there is no value present.
Related
What I'm trying to do is an affiliate program and I wish when I call an scope it returns all my desired values. There will be a logic for calculation of the values for the platform, seller and affiliate.
What I have already is:
public function scopeProfitPlatform($query) {
return $query->sum('price')*0.6;
}
public function scopeProfitAffiliate($query) {
return $query->sum('price')*0.05;
}
public function scopeProfitBuyer($query) {
return $query->sum('price')*0.35;
}
This works, however I'd like to make this more simple. So instead of having to call Order::profitplatform() and order::profitaffiliate, etc.... I wanted to call Order::profit() and it would return all those values to me all at once.
I tried to use Order::profitplatform()->profitaffiliate() but it doesn't work.
I was thinking of creating a specific service for this purpose however I don't think it's necessary. I think it's possible to with scopes or something else similar, however I'm not finding the solution.
I appreciate any suggestions, ideas and solutions for simplifying this problem. Thanks.
try this:
Order::profitplatform()->profitaffiliate()->get();
Is it possible to change an attribute before it's value is returned in Laravel?
I have 2 ways of finding a sell price, the one implemented is just getting the price from the database, and the other is calculating it from a buy price with currency and advance.
Throughout the system i pull the price like this:
$product->baseprice;
Is it possible to run a model function like this:
public function baseprice(){
if(is_null($this->baseprice){
return $this->calculatePrice();
}
return $this->baseprice;
}
It's always one way or the other, baseprice will always be null if the sellprice should be calculated.
when using $this->baseprice?
Comments are welcome if this is bad practice.
What you're looking for is an accessor. The Laravel documentation has plenty of good examples on how to use these, but in a nutshell you define a method in the form get[Whatever]Attribute and it allows you to call $model->whatever and get the output of that method.
So, your code will be something like this:
public function getBasepriceAttribute()
{
if (is_null($this->attributes['baseprice'])) {
return $this->calculatePrice();
}
return $this->attributes['baseprice'];
}
You can change an attribute before it's value is returned from model by Accessors
I have been trying to use a Boolean value in laravel TINYINT(1). However although it works fine for true/1 it cannot understand false/0. So if I change it to false it will still read as true as it seems Eloquent ignores the false value.
I have tried strings, ints and normal Boolean but they all have the same effect.
// I pull in the info kind of like this
$fields = Input::all();
$model->never_expires = $field['never_expires'];
$model->save();
never_expires is fillable within the model and uses a laravel boolean (created from the schema generator) which translates to a TINYINT(1).
I'm sure its something super simple I am missing, but I just can't see it.
I found that only by manually adjusting the Boolean row it would actually update, so I added the following to the model.
public function setExpire($never_expires)
{
if ($never_expires == 1)
{
DB::table($this->table)->where('id', $this->id)->update(['never_expires' => 1]);
}
else
{
DB::table($this->table)->where('id', $this->id)->update(['never_expires' => 0]);
}
return true;
}
protected function getHasExpireAttribute()
{
return $this->never_expires;
}
Basically I used the public function setExpire to handle adding the expiry and use has_expire as a fake element to get it (to ensure that I know its only my function saving anything to that column).
Very dirty way to do it since you basically do 2 mysql queries in one save, but it works.
I won't be using this answer as the answer, I still believe there must be a better way, but spent too long on such a tiny problem.
I needed to create dynamic breadCrumbs that must be realized automatically by the application. So I have the following structure in the URL for navagation:
nav=user.listPMs.readPM&args=5
then i could have a function-file whose sole purpose would be to define the user.listPMs.readPM function itself:
file: nav/user.listPMs.readPM.php
function readPM($msgId)
{
/*code here*/
}
Of course this ends up cluttering the global scope since i'm not wrapping the function withing a class or using namespaces. The best solution here seems to be namespacing it, no doubt right? But I also thought of another one:
file: nav/user.listPMs.readPM.php
return function($msgId)
{
/*code here*/
};
Yep, that simple, the file is simply returning an anonymous function. I think this is amazing because i don't need to care about naming it - since i've already properly named the file that contains it, creating a user function and yet having to name it would seem just redundant. Then in the index I would have this little dirty trick:
file: index.php
if($closure = #(include 'nav/'.$_GET['nav']))
{
if($closure instanceof Closure)
{
$obj = new ReflectionFunction($closure);
$args = explode(',',#$_GET['args']);
if($obj->getNumberOfParameters($obj)<=count($args))
call_user_func_array($closure,$args);
else
die('Arguments not matching or something...');
} else {
die('Bad call or something...');
}
} else {
die('Bad request etc.');
}
Don't even need to mention that the breadCrumbs can be nicely built latter just by parsing the value within the $_GET['nav'] variable.
So, what do you think, is there a better solution to this problem? Have you found another way to explore Closures and/or Reflection?
I like the basic idea. But the implementation is pretty much terrible. Imagine that I set nav=../../../../../../etc/passwd. That would (depending on your server configuration) allow me to access your password file, which certainly is no good.
I'm working on a Symfony project (my first) where I have to retrieve, from my Widget class, a set of widgets that belong to a Page. Before returning the results, though, I need to verify--against an external service--that the user is authorized to view each widget. If not, of course, I need to remove the widget from the result set.
Using CakePHP or Rails, I'd use callbacks, but I haven't found anything similar for Symfony. I see events, but those seem more relevant to controllers/actions if I'm reading things correctly (which is always up for discussion). My fallback solution is to override the various retrieval methods in the WidgetPeer class, divert them through a custom method that does the authorization and modifies the result set appropriately. That feels like massive overkill, though, since I'd have to override every selection method to ensure that authorization was done without future developers having to think about it.
It looks like behaviors could be useful for this (especially since it's conceivable that I might need to authorize other class instances in the future), but I can't find any decent documentation on them to make a qualified evaluation.
Am I missing something? It seems like there must be a better way, but I haven't found it.
First of all, I think behavior-based approach is wrong, since it increases model layer coupling level.
There's sfEventDispatcher::filter() method which allows you to, respectively, filter parameters passed to it.
So, draft code will look like:
<somewhere>/actions.class.php
public function executeBlabla(sfWebRequest $request)
{
//...skip...
$widgets = WidgetPeer::getWidgetsYouNeedTo();
$widgets = $this->getEventDispatcher()->filter(new sfEvent($this, 'widgets.filter'), $widgets));
//...skip...
}
apps/<appname>/config/<appname>Configuration.class.php
//...skip...
public function configure()
{
$this->registerHandlers();
}
public function registerHandlers()
{
$this->getEventDispatcher()->connect('widgets.filter', array('WidgetFilter', 'filter'));
}
//...skip
lib/utility/WidgetFilter.class.php
class WidgetFilter
{
public static function filter(sfEvent $evt, $value)
{
$result = array();
foreach ($value as $v)
{
if (!Something::isWrong($v))
{
$result[] = $v;
}
}
}
}
Hope you got an idea.
Here's some documentation on Symfony 1.2 Propel behaviors: http://www.symfony-project.org/cookbook/1_2/en/behaviors.
Why not just have a 'getAllowedWidgets' method on your Page object that does the checks you're looking for? Something like:
public function getAllowedWidgets($criteria = null, PropelPDO $con = null) {
$widgets = $this->getWidgets($criteria, $con);
$allowed = array();
// get access rights from remote service
foreach($widgets as $widget) {
// widget allowed?
$allowed[] = $widget;
}
return $allowed;
}
However, if you always want this check to be performed when selecting a Page's Widgets then Propel's behaviours are your best bet.
Although, at least in theory, I still think that a behavior is the right approach, I can't find a sufficient level of documentation about their implementation in Symfony 1.4.x to give me a warm and fuzzy that it can be accomplished without a lot of heartburn, if at all. Even looking at Propel's own documentation for behaviors, I see no pre- or post-retrieval hook on which to trigger the action I need to take.
As a result, I took my fallback path. After some source code sifting, though, I realized that it wasn't quite as laborious as I'd first thought. Every retrieval method goes through the BasePeer model's doSelect() method, so I just overrode that one in the customizable Peer model:
static public function doSelect( Criteria $criteria, PropelPDO $con = null ) {
$all_widgets = parent::doSelect( $criteria, $con );
$widgets = array();
foreach ( $widgets as $i => $widget ) {
#if( authorized ) {
# array_push( $widgets, $widget );
#}
}
return $widgets;
}
I haven't wired up the service call for authorization yet, but this appears to work as expected for modifying result sets. When and if I have to provide authorization for additional model instances, I'll have to revisit behaviors to remain DRY, but it looks like this will suffice nicely until then.