Proper Use of Accessors in Laravel - laravel

I am new to Laravel and am building a simple CRUD app to learn more about the framework. I am curious about the proper use of accessors.
I thought accessors would be great for formatting a model's properties for display in a view, much like a filter in Angular. Currently I have a few accessors set to convert char(1) fields to full values in the view, like "c" to cash or "f" to financed. Is this the intended (or an acceptable) use of accessors? If so, what is a good way to prevent accessors from formatting properties that are binded to a form, for instance, in the edit route.
For example, I am storing a monetary amount in the db as a decimal but formatting it with characters ($150,00) for display in the show route. How can I prevent the accessor from altering the value when populating the edit form? (Validation will fail as the input is limited to numeric values).
http://laravel.com/docs/4.2/eloquent#accessors-and-mutators
http://laravel.com/docs/4.2/html#form-model-binding

Everything depends on your needs. The key is that you don't need to create accessors to actual columns/properties. For example let's assuyme in DB you have price field.
Using the following code:
$model = Model::find(1);
echo $model->price;
You can display row price just to display data from database.
But you can also create accessor for unexisting property:
public function getCurPriceAttribute($value)
{
return '$ '.($this->price * 1.08); // for example adding VAT tax + displaying currency
}
now you can use:
$model = Model::find(1);
echo $model->price;
echo $model->cur_price;
Now if you want to put data into form you will use $model->price to allow user to change it without currency and in other places where you want to display product value with currency you will use $model->cur_price

Related

Laravel populate form with cloned model data

I'm implementing a "Clone" button in my application, which should allow to perform the following:
create a copy of the chosen model;
redirect to the create view, whose form field should be populated with the cloned model's data;
allow the user edit some fields;
save the new model.
So far, my ModelController#clone method is:
$newModel = $existingModel->replicate();
$newModel->title = "Copy of ".$existingModel->title;
$newModel->created_at = now() // not sure if necessary, or if it'll be changed once the model is stored in the database
return redirect(route('models.create')); // I know this doesn't do what I need
As it is, obviously, nothing gets passed to the create view, but I can't find any clue on how to do that.
I have tried adding ->withInput(compact($newModel)) to the redirect() call, but I don't see the field being populated.
In the models.create view, I have set up the form field to use the old(...) data, if available.
This answer is almost what I need, but it would imply changing every field to check if there is some sort of input other than the old session data, like this:
<input [other attributes omitted] value="{{ $newModel['title'] ?? old('title') }}">
Is it the right way to do so, or is there a quicker/more standardized way of proceeding?
you could overriding the session old input data by:
Session::put('_old_input', $newModel);
and then just render the old() in form inputs

Laravel validation; human names for array fields

In Laravel form validation you can do this: file_description.* to validate each item of an array according to a set of rules. The problem is, the system automatically returns "file_description.1 is required" as an error message if the field is required and not filled in.
More on that here: https://ericlbarnes.com/2015/04/04/laravel-array-validation/
Now, I'm not a complex man, I just want the field to say "File Description 1 is required". I am aware you can set messages but a) my input arrays are dynamically generated by jquery (click to add more type scenario) so I'd have to use a loop like in the above example b) I feel like there must be a better way.
Is there a way to either extend the core validation system to simply return a humanized name for the array field as Laravel does with regular fields, or is there an option I missed in the docs that allows for this? I'd rather not get involved with doing some regex type search to fix this.

Why is my Laravel Eloquent accessor not showing up in the response?

I have a Model Review that has a unix timestamp as 1 of it's attributes (table columns).
I use 2 accessors inside this model:
public function getReviewDateAttribute($value)
{
return strftime('%A %e %B %Y', $value);
}
public function getReviewDateIsoAttribute()
{
return Carbon::createFromTimestamp($this->review_date)->toDateTimeString();
}
getReviewDateAttribute works as expected and shows up in the collection of models when I write a query.
However getReviewDateIsoAttribute does not. What could be the reason for this?
A subquestion: If I use the same attribute in both functions, how can I use the original format as input value?
You should be adding it to the $appends array. It isn't spinning through all available methods looking for getXXXXXAttribute. The other one is used because it is an accessor for an actual attribute, this one is not an actual attribute.
class YourModel ....
{
protected $appends = ['review_date_iso'];
...
}
Laravel 5.5 Docs - Eloquent - Serialization - Appending Values to JSON
This is probably because ReviewDateIso is not an actual column and therefore will not show up in the model collections...you can access it directly by calling the method directly
$model->getReviewDateIsoAttribute()
According to the docs accessors a ways of changing the value of a column before it is returned to the method that queried it.
If you want it to show up when you call the collection as Json append it to the output with
protected $appends = ['name_of_attribute'];
I tried queries in tinker and with dd method. So I got confused since I cant view my new accessor in the attribute list.
I thought I missed something. Then later I noticed appended attributes are shown separately when we dd the query result than the usual attribute JSON.
I lost time thinking my accessor is not working. Its just I`m not checking in the right way or right place. So I``m attaching the following image, if anyone gets stuck on accessors like me, this might help them.
In fact it depends on what you are trying to achieve. If you are going to display anything in Blade for example, you can use whenever you want $object->review_date_iso to get this attribute value.
However if you are returning Json responses (for example API), you need to tell Eloquent model to append extra fields to your model when transforming to JSON format.
You probably have review_date attribute in your model (this is the column in database I suppose) but you don't have review_date_iso in your table in database, so in your model you need to use:
protected $appends = ['review_date_iso'];
but you don't have to include here review_date because it's already in your table in database and your accessor will be automatically fired.

Laravel - Model binding not working when I use accessors

I want to populate my select box from the Laravel IoC Container.
App\Http\Utilities\BillingHelper.php
views/billing/create.blade.php
views/billing/edit.blade.php
Create the table:
Now, instead of the value, i want to display some flags and currency symbols.
Should i use mutators?
Problem
If i use mutators, when i open the edit page, i see always the first value selected, from the BillingHelper, instead of the choosen one.
Any help? Thanks.
I know it should be a comment, but I have no reputation.
What if, on you edit page, you replace null on Form::select with $client->language and $client->currency.
I know that you area binding the values with Form::Model. But worth a try.
When you use mutators the matching won't occur anymore. You'll have to use a matching static array according to the values you'll return. (the flags)
You can make it work if you make a mutator for saving the data also and simplify again to the ['it', 'en', 'lv'], otherwise your saved data will differ and the initial mutator won't work the second time. You can still make a one-time-only test.
This is why:
Your form binding is using $bill->language to retrieve the actual stored data, and compare it with the values in your $bill::lang static array. If no match found, than the first value will be always selected.
Can you provide the the currency and language fields definition in the migration for the bill?
Also retrive your values from your bills DB and paste them here for language and currency. They must be in the defined static sets.
Laravel has a way of skipping the accessor/mutator by using
$model->getOriginal('data_field').
The "getOriginal()" gets the model's original attribute values.

Other way to pass data from block to controller Magento

I'm pretty new to PHP programming and Magento. I wanna to pass the current ProductId from a form within a custom block to a controller (new action).
Yes I know that one method would be to add an input hidden (with my product id) in the custom block form and then to retrieve the Value through a regular:
$this->getRequest()->getPost('myvalue'))
Is there a better way in Magento to retrieve the value within the controller without having to declare extra secret input fields ?
Good for you for wanting to adhere to best practices within Magento! The passing of data to controllers is pretty standard, however. If we look at how the product is added from a product page, we'll actually see the product ID in the form action URL's parameters:
http://domain.com/checkout/cart/add/uenc/uenc_value/product/45573/
...where 45573 is the product ID. Of course this can also be sent to the controller via a hidden input field, which I use all the time. Note that the above is the same as http://domain.com/checkout/cart/add/?uenc=uenc_value&product=45573 in Magento.
Another way of storing data for use in controllers for future use is setting data into a session. For posting data to a controller I wouldn't recommend this method but it's something to keep in mind:
$session = Mage::getSingleton('core/session');
$session->setMyValue(true);
We can then retrieve the data from my_value later just by instantiating the session. Good luck!
Passing your data could be done in different ways :
You could use Magento's magic setters and getters.
So you would have to do this to set the value :
Mage::getSingleton('core/session')->setSomeVariable($value);
and this to retrieve it :
Mage::getSingleton('core/session')->getSomeVariable();
Or you could use the register.
Mage::register('key', $value); //to set your data
Mage::registry('key'); //to get your data
Magento provides a way to construct a URL with the necessary values, calculated against the configuration DOM. Blocks (and therefore block templates) can call Mage_Core_Block_Abstract::getUrl() directly:
$this->getUrl('some_handle/foo/test',array('id'=>'some_value'));
// Mage::getUrl() will work as well
The above would result in the following URL:
http://base_url/frontname/foo/action/id/some_value/
...which can be read in the FooController testAction() as
$this->getRequest()->getParam('id') // 'some_value'

Resources