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')
Related
In a Laravel project I have to store some data in json. For phpUnit tests I use a factory with faker. I try to fake a json structure for the tests, but it always fail on validation. Is there any proper way to create a json in factory that passes the validation for json?
I tried a simple json array, and the json array with json_encode, both of them failed at validation, and gives errors.
With simple json like: 'settings' => ['areas' => ['full', 'city']]
the error is:
Property [settings] is not of expected type [json].
Failed asserting that an array contains 'array'.
With json_encode like: 'settings' => json_encode(['areas' => ['full', 'city']])
the error is:
Property [settings] is not of expected type [json].
Failed asserting that an array contains 'string'.
My model:
class Example extends Model
{
protected $fillable = [
'name',
'settings'
];
public static $rules = [
'name' => 'required|string|max:255',
'settings' => 'nullable|json'
];
protected $casts =
'settings' => 'array'
];
}
My factory:
<?php
class ExampleFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Example::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->words(3, 7),
'settings' => json_encode(['areas' => ['full', 'city']]) // or what?
];
}
}
In my test file:
/** #test */
public function shouldStore(): void
{
$item = $this->model::factory()->make();
$data = $item->toArray();
$this->post(action([$this->controller, 'store']), $data)
->assertOk();
}
Your issue is that you are casting the property to array, but you are storing a string, you should be passing an array.
Do this:
class ExampleFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Example::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->words(3, 7),
'settings' => ['areas' => ['full', 'city']],
];
}
}
And the rule (no idea where are you using that) should be like this:
'settings' => 'nullable|array'
Your model works just fine with an array because the cast takes care of the conversion for you. However when posting to the controller you need to manually cast the data to JSON:
public function shouldStore(): void
{
$item = $this->model::factory()->make();
$data = $item->toArray();
$data['settings'] = json_encode($data['settings']);
$this->post(action([$this->controller, 'store']), $data)
->assertOk();
}
However this does mean that you may need to json_decode the data in the controller before creating the model.
Alternatevely you can do what #matiaslauriti is suggesting and post the data as an array to begin with
You need to cast the property to array:
SomeModel extends Model
{
protected $casts = [
'settings' => 'array',
];
}
I'm trying to run a faker factory for relationships, but the field always returns NULL. How do fake a model relationship without hitting the database?
I have a Map factory with a one-to-one relationship to a parent Event table. I need to fake this relationship for unit testing:
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'event' => 'faker.' . join('_', $this->faker->words),
'category' => $this->faker->word,
'sub_category' => $this->faker->word,
'priority' => $this->faker->randomElement(['normal', 'high']),
'event' => Event::factory()->makeOne(),
];
}
This returns a fake model, but event is null, from the debugger:
result = {array} [5]
event = "faker.eum_voluptatibus_aut"
category = "libero"
sub_category = "aut"
priority = "high"
event = null
I tried using states, but the same thing happens:
public function disabled()
{
return $this->state([
'event' => Event::factory()->makeOne(['enabled' => false]),
]);
}
The object is returned with an empty event value. I need a faker object I can transverse down into: if ($object->event->enabled) [...]. How do I generate fake model relationships?
If you are using Laravel 8.x you must consider using methods used on the docs, it must look like that :
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'event' => 'faker.' . join('_', $this->faker->words),
'category' => $this->faker->word,
'sub_category' => $this->faker->word,
'priority' => $this->faker->randomElement(['normal', 'high']),
'event_id' => Event::factory(),
];
}
/**
* Indicate that the map is disabled.
*
* #return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function disabled()
{
return $this->state([
'event_id' => Event::factory()->create(['enabled' => false]),
]);
}
The only solution I found, so far, is to manually set the event key myself in my tests. It's not the ideal or elegant solution.
$fieldMap = Map::factory()->makeOne();
$fieldMap->event = Event::factory(['enabled' => false])->makeOne();
I don't like this approach. Why can't I define factories within factories?
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.
I have written like this
/**
* itemRepository
*
* #var \KRT\KrtEmployee\Domain\Repository\ItemRepository
* #inject
*/
protected $itemRepository = null;
/**
* action list
*
* #return void
*/
public function listAction()
{
$arguments =$this->request->getArguments();
$employees = $this->itemRepository->findAll();
$this->view->assign('employees',$employees);
}
In my $employees result I have
Employee ID (Default uid)
Name
Designation
Department
Salary
Now, How can I Sort the result array in ascending order based on
Name
Department and Salary
Is there any default function to sort inside the repository queries?
Every repository has a $defaultOrderings property where you can specify the default orderings applied to all query methods. In your case it could look like this:
protected $defaultOrderings = [
'name' => QueryInterface::ORDER_ASCENDING,
'department.name' => QueryInterface::ORDER_ASCENDING,
'salary' => QueryInterface::ORDER_ASCENDING,
];
As you can see with department.name you can also sort by properties of relations. Notice that this only works for 1:1 and n:1 relations.
In case of custom query methods in your repository you can manually set the orderings directly on the query:
$query->setOrderings([
'name' => QueryInterface::ORDER_ASCENDING,
'department.name' => QueryInterface::ORDER_ASCENDING,
'salary' => QueryInterface::ORDER_ASCENDING,
]);
You have multiple options depending on what you would like to achieve:
Setting a default order for the entire repository
Add the following to your repository class
protected $defaultOrderings =
array(
'department' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
'salary' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
);
This will apply to all queries made in this repository.
see: https://wiki.typo3.org/Default_Orderings_and_Query_Settings_in_Repository
Setting an order for a single query
Add this to a query you define in your repository
$query->setOrderings(
array(
'department' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
'salary' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
)
);
In this way you could (and would have to) implement a different access method for each sort order you would like to have returned.
see: https://docs.typo3.org/typo3cms/ExtbaseFluidBook/6-Persistence/3-implement-individual-database-queries.html
Sort the result
You can always use PHP sorting methods to sort the query result (possibly converting it to an array with ->toArray() first.
In general and complete:
<?php
namespace <yourVendor>\<yourExtensionkey>\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
class <yourDomain>Repository extends \TYPO3\CMS\Extbase\Persistence\Repository {
protected $defaultOrderings = [
'<yourProperty1>' => QueryInterface::ORDER_ASCENDING,
'<yourProperty2>' => QueryInterface::ORDER_DESCENDING
];
}
Further explanation in docs.typo3.org
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