0

I need to format a string passed from controller once it's in the view while a foreach loop runs. Example:

Controller:

class MyController {
public function index()
{
    $model = Events::all();
    return View::make('foo.index')->with('model', $model);
}

View:

@foreach($model as $item)
<div>{{$model->timestamp}}</div>
@endforeach`

Here's the tricky part; I want to run a few formatting options before echoing it out. For instance, I need to echo out the months from the timestamp in french. Since I'm trying to avoid having any code in the view, where do I put this and how do I call it?

Example formatting:

$month = date_parse($model->timestamp);
switch ($month['month']) {
      case '1':
        $month = 'Janvier';
        break;
      case '2':
        $month = 'Février';
        break;
      ...
    }

I know this would work, but is obviously bad code practice:

@foreach($model as $item)
$month = date_parse($item->timestamp);
switch ($month['month']) {
      case '1':
        $month = 'Janvier';
        break;
      case '2':
        $month = 'Février';
        break;
      ...
    }
<div>{{ $month }}</div>
@endforeach`

Any help or tips would be appreciated! Note: I know usually you would take care of formatting in the controller, but since I'm running I can't "pre-format" the output until I run the foreach loop, I'm a bit at a loss here about best convention.

Thanks!

ayame
  • 87
  • 8

3 Answers3

1

One of the options to format data before displaying is to process it in your model by defining functions named getXXXAttribute, where XXX is the name of a column of a table.

Let's say you want to format a property called created_at in one of your models. So in that model you can define a function like this:

/**
 * Return creation date in Australian format, i.e. dd/mm/YYYY.
 *
 * @param $value
 * @return bool|string
 */
public function getCreatedAtAttribute($value)
{
    return date('d/m/Y H:i:s',strtotime($value));
}

Please note that the naming convention of such functions uses camelCase.

And when you print out $model->created_at in your view, the data has been formatted.

Back to your scenario, you can define a function getTimestampAttribute() in your model, then put all your formatting logic into it.

In some cases where you want to have both original data and formatted data, you can add one more property in your model. For instance:

protected  $appends = array('parsed_timestamp');

public function getParsedTimestampAttribute()
{
    $original = $this->attributes['timestamp'];
    // process original timestamp...
}

In your view simply access this new property in this way : $model->parsed_timestamp.

For your reference, here's the official document

By the way, you may want to construct a file in app/lang to handle i18n problem, getting rid of hard coding your French string.

Hope this help!

Carter
  • 1,230
  • 13
  • 24
  • Wow cool! Didn't know laravel could do that. There's a small problem though: I need to echo out the non-formatted version on the page as well. Can this be done using this trick? Can I give a separate access value to each? Example {{ $model->created_at1 }} and {{ $model->created_at2 }} ? – ayame Nov 20 '14 at 04:35
1

As Carter mentioned, you can define functions in your model. Here is a more specific approach:

in your model add

public function getLocalizedMonth($locale = 'fr_FR')
{
    setlocale(LC_TIME, $locale);

    // for the column "created_at"
    $month = strftime("%B", $this->created_at->timestamp);

    // or for your column "timestamp"
    //$month = strftime("%B", $this->timestamp->timestamp);

    // set back to default 'en_US'
    setlocale(LC_TIME, 'en_US');

    return $month;
}

"created_at" ist handled as instance of Carbon. Use the following function in your model to handle the column "timestamp" as well:

public function getDates()
{
    return array('created_at', 'updated_at', 'timestamp');
}

in your view use

for french {{ $item->getLocalizedMonth() }}

or maybe dutch {{ $item->getLocalizedMonth('nl_NL') }}

remarks

The view in your question should look like this, because I think you want the timestamp from the $item:

@foreach($model as $item)
    <div>{{ $item->timestamp }}</div>
@endforeach

Finaly with the new function:

@foreach($model as $item)
    <div>{{ $item->getLocalizedMonth() }}</div>
@endforeach

The PHP-Manual for setlocale() says:

Warning The locale information is maintained per process, not per thread. If you are running PHP on a multithreaded server API like IIS, HHVM or Apache on Windows, you may experience sudden changes in locale settings while a script is running, though the script itself never called setlocale(). This happens due to other scripts running in different threads of the same process at the same time, changing the process-wide locale using setlocale().

last but not least

On a windows machine the $locale strings may vary: "fr-FR" or "nl-NL"

slp
  • 376
  • 3
  • 5
0

If this is the only thing that needs to be in French, I would probably extend Blade and perhaps add a @frenchMonth directive or something, have a look at this example from the docs, otherwise, I would have a look at using the setlocale and strftime functions instead of date(), more on that here


The following example creates a @datetime($var) directive which simply calls ->format() on $var:

Blade::extend(function($view, $compiler)
{
    $pattern = $compiler->createMatcher('datetime');

    return preg_replace($pattern, '$1<?php echo $2->format(\'m/d/Y H:i\'); ?>', $view);
});
bruchowski
  • 5,043
  • 7
  • 30
  • 46
  • Sounds like what I need. I'm a bit puzzled on how to use this though. Where do I add this code? How do I call it in the view? Do you have a simple example of it in use? – ayame Nov 20 '14 at 03:22
  • Have a look at [this SO answer](http://stackoverflow.com/questions/18329541/where-to-place-bladeextend), basically anywhere you want to put it, `start/global.php` is probably good – bruchowski Nov 20 '14 at 03:28