laravel problems with mutators - php

My journey into laravel 4 (from laravel 3) continues....
I have an Article model, accessing a table called articles.
I have set up the model with the following mutators:
class Article extends Eloquent {
public function getArticleDateAttribute($value)
{
return date('d/m/Y', strtotime($value));
}
public function getValidUntilAttribute($value)
{
return date('d/m/Y', strtotime($value));
}
}
Now when I query the database with the following AND Delete the mutators everything works as expected and I get the data I expect:
public function getTest() {
$data = Article::select(array(
'articles.id',
'articles.article_date',
'articles.image_link',
'articles.headline',
'articles.category'
)) ->get()
->toArray();
var_dump($data);
//return View::make('_layouts.master');
}
In my test I get the results as expected as this sample:
array (size=5)
'id' => int 3
'article_date' => string '2008-06-03 00:00:00' (length=19)
'image_link' => string '' (length=0)
'headline' => string 'Sussex Amateur Course Closure' (length=29)
'category' => int 6
Now, when I add back the mutators, with the exact query I get the following data:
array (size=6)
'article_date' => string '03/06/2008' (length=10)
'valid_until' => string '01/01/1970' (length=10)
'id' => int 3
'image_link' => string '' (length=0)
'headline' => string 'Sussex Amateur Course Closure' (length=29)
'category' => int 6
the column order is changed and it's included a column I didn't originally request. How should I correctly implement mutators and why do the columns change?
Have I misunderstood this?
Thanks
Ray

The mutators will be called, because the code is built that way. See the implementation of this function in the Eloquent Model class (which is called by toArray()):
/**
* Convert the model's attributes to an array.
*
* #return array
*/
public function attributesToArray()
{
$attributes = $this->getAccessibleAttributes();
// We want to spin through all the mutated attributes for this model and call
// the mutator for the attribute. We cache off every mutated attributes so
// we don't have to constantly check on attributes that actually change.
foreach ($this->getMutatedAttributes() as $key)
{
if ( ! array_key_exists($key, $attributes)) continue;
$attributes[$key] = $this->mutateAttribute($key, $attributes[$key]);
}
return $attributes;
}
https://github.com/laravel/framework/blob/master/src/Illuminate/Database/Eloquent/Model.php

Related

How to store an array as a string in attribute

I want to store an array as a string in a open_hours field. How i can do it.
Please help me.thats my code . Thanks in advance
$company = Company::create([
'company_name' => $request->input('company_name'),
'company_picture'=> $company_picture,
'address' => $request->input('address'),
'latitude' => $request->input('latitude'),
'longitude' => $request->input('longitude'),
'zipcode' => $request->input('zipcode'),
'city' => $request->input('city'),
'country' => $request->input('country'),
'open_hours' => $request->input('open_hours'),
'subcategory_id' => $request->input('subcategory_id'),
'price' => $request->input('price'),
'age_limit' => $request->input('age_limit'),
'company_description' => $request->input('company_description'),
]);
You could store it a JSON in the database and make Laravel to handle it (cast) as an array.
From the documentation:
Array & JSON Casting
The array cast type is particularly useful when working with columns
that are stored as serialized JSON. For example, if your database has
a JSON or TEXT field type that contains serialized JSON, adding
the array cast to that attribute will automatically deserialize the
attribute to a PHP array when you access it on your Eloquent model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'options' => 'array',
];
}
Once the cast is defined, you may access the options attribute and
it will automatically be deserialized from JSON into a PHP array. When
you set the value of the options attribute, the given array will
automatically be serialized back into JSON for storage:
$user = App\User::find(1);
$options = $user->options;
$options['key'] = 'value';
$user->options = $options;
$user->save();
So, you can do this in your Company model:
app/Company.php
class Company extends Model
{
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'open_hours' => 'array',
];
}
Of course, you should have the open_hours column defines as a JSON/TEXT column:
database/migrations/create_companies_table.php // your migration file
public function up()
{
Schema::create('companies', function (Blueprint $table) {
// ...
$table->json('open_hours');
// ...
});
}
Update
Now, whenever you get a Company instance, you should be able to handle your desired field as an array:
ACoolController.php
// get your instance
$company = App\Company::find(1);
// return it to a view
return view('path.to.view')->with('company', $company);
Then in your view:
path/to/view.blade.php
#foreach ($company->open_hours as $open_hour)
<p> This is an {{ $open_hour }} </p>
#endforeach
Use open_hours column as json to store array values. Laravel migration has column type ->json('column_name')

How do you convert third party API data to a collection resource in Laravel 5.6?

I've been working on creating a clean interface for our various web application and I've run into a snag with Laravel's API Resources not properly converting the incoming json array into laravel collections.
I can do it with a single resource:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
use App\Product;
class ProductResource extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'name' => $this->resource['product']['name'],
'description' => $this->resource['product']['description'],
'price' => $this->resource['product']['rental_rate']['price']
];
//return parent::toArray($request);
}
}
print this response outputs:
{"name":"Arri Skypanel S60-C","description":"Arri Sky Panel S60-C 450w input with a 2000w tungsten equivalent & Combo Stand","price":"260.0"}
However trying to take this single item and turn it into a collection of items isn't going anywhere.
Anybody got a clue what I'm missing?
Pulling the API data looks like this:
namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
class ThirPartyAPI
{
private $url = 'https://api.third-party.com/api/v1/';
public function pull($query, $additionalParams) {
$client = new Client;
$result = $client->get($this->url . $query . $additionalParams, [
'headers' => [
'Content-Type' => 'application/json',
'X-AUTH-TOKEN' => env('CURRENT-AUTH-TOKEN'),
'X-SUBDOMAIN' => env('CURRENT-SUBDOMAIN')
]
]);
$array = json_decode($result->getBody()->getContents(), true);
return $array;
}
}
The API returns a lot of json data.
This is the Product model:
public function getAllProducts() {
try {
$productData = [];
$query = "/products?page=1&per_page=3&filtermode=active";
$additionalParams = "";
$productData = new ThirdPartyAPI;
$productData = $productData->pull($query, $additionalParams);
$productData = $productData['products'];
return ProductsResource::make($productData);
} catch (\Exception $ex) {
return $ex;
} catch (\Throwable $ex) {
return $ex;
}
}
Right now I'm trying something this to convert all the returned arrays into something I can control more:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class ProductsResource extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'products' => $this->collection->mapInto(function($request) {
return[ 'name' => $this->resource['name'],
'description' => $this->resource['description'],
'price' => $this->resource['rental_rate']['price']
];
})
];
}
However var_dumping the data just returns this:
object(App\Http\Resources\ProductsResource)[200]
public 'resource' =>
array (size=3)
0 =>
array (size=37)
'id' => int 164
'name' => string '10A Dimmer' (length=10)
[Lots of data]
...
'sale_rates' =>
array (size=0)
...
1 => .....
[cont]
public 'with' =>
array (size=0)
empty
public 'additional' =>
array (size=0)
empty
I've tried various forms of data conversion on the return json info and haven't had a lot of results except errors and confusing business. I'm a little shady on how Laravel handles API Resource handling.
Ok after some investigation into Laravel's 'make', 'mapinto' and 'map' methods for collections, I eventually got a working result from this conversion here:
$productData = ThirdPartyAPI;
$productData = $productData->pull($query, $additionalParams);
$productData = $productData['products'];
$products = collect($productData)->map(function($row){
return ProductsResource::make($row)->resolve();
});
var_dump($products);
That var_dump returns this:
object(Illuminate\Support\Collection)[228]
protected 'items' =>
array (size=3)
0 =>
array (size=3)
'name' => string '10A Dimmer' (length=10)
'description' => string '10amp Dimmer (Max 2.4k)' (length=23)
'price' => string '5.0' (length=3)
....
[And so on]
The initial information that was returned was a multidimensional array.
$returnedArray = array(
['array' => 1, 'name' => 'name', etc],
['array' => 2, 'name' => 'name, etc]
);
Laravel's default collection method only turns the top array into a collection. In order to properly be able to control the results via the Resource models we have to convert the whole set of arrays to collections, which means we have to iterate through the returned data to convert it to something laravel can read properly. That's what the map method does.
According to the docs it, 'The map method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items'
the make method creates a new collection instance. I don't know what the resolve function does except that the docs mention that it 'resolves a given class or interface name to its instance using the service container'. I'm going to assume it means that it makes sure passes through the class properly?
Anyway I hope that helps people in the future.

How to convert Doctrine array to PHP associative array

I'm integrating Doctrine2 into CodeIgniter.
My Entity class News.php
<?php
namespace Models\Entities;
/**
* News
*
* #Table(name="news", indexes={#Index(name="slug", columns={"slug"})})
* #Entity
*/
class News {
//HERE: properties, getter, setter, etc.
}
And my model class News_model.php
<?php
require_once(APPPATH."models/entities/News.php");
use Models\Entities\News;
class News_model extends CI_Model {
//Model code here
}
When I use $news = $this->em->getRepository('Entities:News')->findAll() in News_model class and printed, var_dump($news), I get an array of object(Models\Entities\News), like follow:
array (size=6)
0 =>
object(Models\Entities\News)[87]
private 'id' => int 1
private 'title' => string 'text here' (length=9)
private 'slug' => string '' (length=0)
private 'text' => string 'text here' (length=9)
private 'news' => null
)
But I expected an associative array, like follow:
array (size=6)
0 =>
array (size=4)
'id' => string '1' (length=1)
'title' => string 'text here' (length=9)
'slug' => string '' (length=0)
'text' => string 'text here' (length=9)
)
How can I convert the Doctrine Entity object (first showed array) result to a PHP associative array (second showed array)?
You are working with Doctrine ORM. ORM means Object Relational Mapper. You use ORM because you want to get results as objects. Otherwise you better start to read about Doctrine DBAL.
Then this line:
$news = $this->em->getRepository('Entities:News')->findAll();
If you use findAll() then you expect an Collection of objects. In Doctrine we talk about collections instead of array's.
Those collections you can simple walk through with a foreach just like a normal array. Then you can use each object inside the collection which has some benefits: especial directly call some custom methods
$newitems = $this->em->getRepository('Entities:News')->findAll();
foreach($newsitems as $newsitem)
{
echo '<h3>' . $newsitem->getTitle() . '</h3>';
}
why don't you use native doctrine method getArrayResult in your class repository?
In your controller :
/***/
$news = $this->em->getRepository('Entities:News')->yourMethodName();
/***/
In your class Repository :
class NewsRepository extends \Doctrine\ORM\EntityRepository
{
public function yourMethodName()
{
$query = $this->createQueryBuilder('n');
/***/
return $query->getQuery()->getArrayResult();
}
}
I agree with #Frank B, the reason you use Doctrine is that you get to work with objects instead of a magic array.
However, if you are set on having an array, you can use the Symfony Serializer to convert any object to an array.
Just add some annotations to your entity:
use Symfony\Component\Serializer\Annotation\Groups;
class News {
/**
* #Groups({"group1"})
*/
protected $id;
/**
* #Groups({"group1"})
*/
protected $title;
/**
* #Groups({"group1"})
*/
protected $slug;
}
Then you can convert your array collection like this:
$news = $this->em->getRepository('Entities:News')->findAll();
$serializer = $this->getContainer()->get('serializer');
$newsArray = $serializer->normalize($news, 'json', ['groups' => ['group1']]);

Eloquent Many to Many Attach Function sending a Null ID

I have the following code which is attempting to attach a product to a product_slot.
$product_slot = new ProductSlot;
$product_slot = $product_slot->where('type', $slot['id'])->first();
$product = new Product;
$product = $product->where('type', (String)$allowed_product)->first();
$product->productSlots()->sync([$product_slot->product_slot_id]);
I can do a getAttributes() and both return the following:
array (size=6)
'product_slot_id' => int 1
'type' => string 'breakfastplatter1' (length=17)
'default_product_id' => string 'bigbfhotcakebiscplat_3590' (length=25)
'allow_empty' => int 0
'created_at' => string '2016-08-17 19:04:41' (length=19)
'updated_at' => string '2016-08-17 19:04:41' (length=19)
array (size=7)
'product_id' => int 185
'type' => string 'bigbfhotcakebiscplat_3590' (length=25)
'label' => string 'Big Breakfast with Hot Cakes (Biscuit)' (length=38)
'min_option_slots' => int 0
'max_option_slots' => int 0
'created_at' => string '2016-08-17 19:05:40' (length=19)
'updated_at' => string '2016-08-17 19:05:40' (length=19)
Here is the Product Model relationship:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = ['type', 'label', 'min_option_slots', 'max_option_slots'];
public function productSlots()
{
return $this->belongsToMany('App\ProductSlot');
}
}
However, when I try to sync these models together I get the following error.
SQLSTATE[23000]: Integrity constraint violation: 1048 Column
'product_id' cannot be null (SQL: insert into product_product_slot
(product_id, product_slot_id) values (, 1))
You are using custom primary key names instead of the Laravel convention of using id as the primary key. Try adding this to your models:
class Product {
public $primaryKey = 'product_id';
}
class ProductSlot {
public $primaryKey = 'product_slot_id';
}
Also note that if you don't follow Laravel conventions, you also sometimes need to specify the non-standard primary keys when defining relationship functions:
public function productSlots()
{
return $this->belongsToMany('App\ProductSlot', 'product_slot_id', 'product_id');
}

In a Laravel 5 Collection how do you return an array of objects instead of an array of arrays?

I am using Laravel 5 and a Blade template. In a view I want to iterate over an array of Model objects, not an array of arrays. If I did want to iterate over an array of arrays I would do the following, which works as expected:
$models = Foo::where('id', '>', 5)->get();
return view('home.index', ['models' => $models->toArray()]);
However I want an array of objects with accessible properties. If I were to run:
$models = Foo::where('id', '>', 5)->get();
return view('home.index', ['models' => $models->all()]);
The var_dump would look like this:
object(Illuminate\Support\Collection)[164]
protected 'items' =>
array (size=3)
0 =>
object(App\Foo)[172]
public 'id' => null
public 'foo' => null
private 'created_at' => null
private 'updated_at' => null
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
public 'timestamps' => boolean true
protected 'attributes' =>
array (size=4)
'id' => int 1
'foo' => string 'Foo!' (length=4)
'created_at' => string '2015-02-27 15:44:09' (length=19)
'updated_at' => null
Not only is the Model in an 'items' object the properties are not filled.
In a view I would like to do something like this:
#foreach ($models as $model)
#include('_partial') {
'id' => $model->id,
'foo' => $model->foo,
}
#endforeach
How do I get an array of Models instead of an array of an array of Models?
According to the Docs:
$array = $collection->all();
"The all method returns the underlying array represented by the collection."
Your code is just fine, except you don't need to call toArray on your Eloquent query result. Let me explain what the code does, so you can understand why the following is what you want:
$models = Foo::where('id', '>', 5)->get();
return view('home.index', ['models' => $models]);
The first statememt Foo::where('id', '>', 5)->get(); returns a value of type Illuminate\Support\Collection.
That Collection class holds the collection elements in a protected property called $items (as you could see from your dump protected 'items' =>), which is of type array. The class also implements an interface called IteratorAggregate, which basically means it allows any variable of that type to be iterated using a foreach statement.
In your case this means that, even if $models is of type Illuminate\Support\Collection it will behave as an array when you go over it with foreach:
#foreach ($models as $model)
{
{{ $model->foo }}
}
So in short Collection is an iterable object that can be treated as an array, but is better than an array because if offers extra methods that allow you to manipulate the items from the collection. You can check the Collection API to see a complete list of available methods.
So in reality you're getting an improved array of models.
Also, don't worry that the properties are not filled, they are in fact filled, I just think you're looking in the wrong place.
If you look closely at your var_dump, you'll see that you have some lines that start with public, protected or private. Those keywords mean that those lines contain object properties. In the case of Laravel Eloquent models, the values fetched from the database are not stored directly in the properties that are named like the database columns. The values are in fact stored in a single property called attributes and are fetched using PHP's magic _get. Take a look at the comments on the code below:
object(Illuminate\Support\Collection)[164]
protected 'items' =>
array (size=3)
0 =>
object(App\Foo)[172]
public 'id' => null // <<< THE VALUES ARE
public 'foo' => null // <<< NOT STORED HERE
private 'created_at' => null
private 'updated_at' => null
protected 'connection' => null
protected 'table' => null
protected 'primaryKey' => string 'id' (length=2)
protected 'perPage' => int 15
public 'incrementing' => boolean true
public 'timestamps' => boolean true
protected 'attributes' =>
array (size=4)
'id' => int 1 // <<< THEY ARE HERE
'foo' => string 'Foo!' (length=4) // <<< AND HERE
'created_at' => string '2015-02-27 15:44:09' (length=19)
'updated_at' => null
Laravel does a lot of trickery behind the scenes to allow you to get things done with only a few lines of code. That's why a var_dump will not always display the simple data structures that you might expect.
Figured out the problem. I was explicitly defining the attributes in the Model. Laravel uses __get() in a particular way which causes passed parameters to be overridden whatever attributes are explicitly defined.
In other words, I was getting null values in the partial because the info I was passing to the partial was overridden.

Categories