I have json file in folder database\data\countries.json and I have CountriesTableSeeder.php
I want to seed all data in countries.json into my table Country. But it occurs error Trying to get property of non-object
I have followed the step that I found, but I still got an error.
This is my model:
protected $fillable = ['country_name', 'iso_code'];
This is my seeder code:
public function run()
{
$countryJson = File::get("database/data/countries.json");
$data = json_decode($countryJson, true);
foreach ($data as $obj) {
Country::create(array(
'country_name' => $obj->name, 'iso_code' => $obj->sortname
));
}
}
You need to pass same variable in foreach, also make sure you have name element by checking dd($data); and you need to get array element by $obj['name'] not by $obj->name as it's not object.
$data = json_decode($countryJson, true);
foreach ($data['countries'] as $obj) {
Country::create(array(
'country_name' => $obj['name'], 'iso_code' => $obj['sortname']
));
}
Related
I know the title isn't that understanding but I will try to explain here my problem.
So I have a form that auto generates input fields by jquery and I'm trying to store that data in the db
the blade:
<div>
<input type="text" placeholder="ID" name="myproduct[]"/>
<input type="text" placeholder="Șansa" name="mychance[]"/>
Delete
</div>
My Controller:
class SomeController extends BaseController {
public function someMethod(Request $request) {
...
$items = '{"'.$request->myproduct[0].'":"'.$request->mychance[0].'", "'.$request->myproduct[1].'":"'.$request->mychance[1].'"}';
$case = Cases::create([
'items' => $items
]);
$case->save();
...
}
}
It is kinda working but I want to know how to get all data in $items without creating new variables like $variable[0], $variable[1],2 ,3 for every input I generate
If you can guarantee that you always have the same amount of products and choices you can use array_combine
$myproduct = [1,2,3];
$mychance = ['test1', 'test2', 'test3'];
$items = array_combine($myproduct, $mychance);
// result: [1 => "test1", 2 => "test2", 3 => "test3"]
// encode it to a string with json_encode
// result: "{"1":"test1","2":"test2","3":"test3"}"
$case = Cases::create(['items' => json_encode($items)]);
Additionaly you can cast your items column to an array
In your Cases model add
protected $casts = [
'items' => 'array',
];
Laravel will then automatically serialize it when storing and deserialize it when accessing giving you an array.
then you could just do
$myproduct = [1,2,3];
$mychance = ['test1', 'test2', 'test3'];
$case = Cases::create(['items' => array_combine($myproduct, $mychance)]);
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
You maybe want to do this:
class SomeController extends BaseController {
public function someMethod(Request $request) {
...
$items = [];
foreach($request->myproduct as $i => $myProductSingle) {
$items[$myProductSingle] = $request->mychance[$i];
}
$case = Cases::create([
'items' => json_encode($items)
]);
$case->save();
...
}
}
Hope it helps..
$items = [];
foreach($request->myproduct as $key => $myProduct) {
$items[$myProduct] = $request->mychance[$key];
}
I have following method in a controller
public function store(Request $request)
{
$site = Site::create([
"path" => $request->path,
"site_link" => $request->link,
]);
if ($request->features) {
$features = explode(',', $request->features);
foreach ($features as $feature) {
$site->features()->save(SiteFeature::create(["feature" => $feature]));
}
}
return response()->json($site, 201);
}
Site model has this method
public function features()
{
return $this->hasMany('App\SiteFeature');
}
And this is my $fillable property of a SiteFeature
protected $fillable = ['feature', 'site_id'];
By some reason I get next error
local.ERROR: SQLSTATE[HY000]: General error: 1364 Field 'site_id'
doesn't have a default value (SQL: insert into site_features
(feature) values (fes)) {"exception":"[object]
(Illuminate\Database\QueryException(code: HY000): SQLSTATE[HY000]:
General error: 1364 Field 'site_id' doesn't have a default value (SQL:
insert into site_features (feature) values (fes))
The Model::create method actually creates a record in your database and since you're not specifying a required site_id, it is failing which is why you're seeing this error.
It looks like you're trying to use Laravel's relationships to save a new SiteFeature for the site but you're passing what would be an already existing object (if the query didn't fail) to the relation's save method.
You need to either pass a new instance of SiteFeature, that has not already been saved to the database to the save method:
$this->features()->save(new SiteFeature(['feature' => $feature]));
or you can use the relation's create method to avoid having to pass in an instance altogether, you just need to provide the attributes:
$this->features()->create(['feature' => $feature]);
try this
in your model features
public function features()
{
return $this->hasMany('App\SiteFeature','site_id','id');
}
you model SiteFeature
public function siteFeatures()
{
return $this->belongsTo('App\features', 'site_id', 'id');
}
$site = Site::create([
"path" => $request->path,
"site_link" => $request->link,
]);
Doesn't return the last insert id... try
$site = new Site;
$site->path = $request->path;
$site->site_link = $request->link;
$site->save();
As for this part:
if ($request->features) {
$features = explode(',', $request->features);
foreach ($features as $feature) {
$site->features()->save(SiteFeature::create(["feature" => $feature]));
}
}
You could do this if the above code doesnt work...
if ($request->features) {
$features = explode(',', $request->features);
foreach ($features as $feature) {
$sf = new SiteFeature;
$sf->feature = $feature;
$sf->site_id = $site->id;
$sf->save();
}
}
I'm new to Laravel and at the moment I have a piece of code in a Controller which without the while loop it works, it retrieves my query from the database.
public function dash($id, Request $request) {
$user = JWTAuth::parseToken()->authenticate();
$postdata = $request->except('token');
$q = DB::select('SELECT * FROM maps WHERE user_id = :id', ['id' => $id]);
if($q->num_rows > 0){
$check = true;
$maps = array();
while($row = mysqli_fetch_array($q)) {
$product = array(
'auth' => 1,
'id' => $row['id'],
'url' => $row['url'],
'locationData' => json_decode($row['locationData']),
'userData' => json_decode($row['userData']),
'visible' => $row['visible'],
'thedate' => $row['thedate']
);
array_push($maps, $product);
}
} else {
$check = false;
}
return response()->json($maps);
}
I am trying to loop through the returned data from $q and use json_decode on 2 key/val pairs but I can't even get this done right.
Don't use mysqli to iterate over the results (Laravel doesn't use mysqli). Results coming back from Laravel's query builder are Traversable, so you can simply use a foreach loop:
$q = DB::select('...');
foreach($q as $row) {
// ...
}
Each $row is going to be an object and not an array:
$product = array(
'auth' => 1,
'id' => $row->id,
'url' => $row->url,
'locationData' => json_decode($row->locationData),
'userData' => json_decode($row->userData),
'visible' => $row->visible,
'thedate' => $row->thedate
);
You're not using $postdata in that function so remove it.
Do not use mysqli in Laravel. Use models and/or the DB query functionality built in.
You're passing the wrong thing to mysqli_fetch_array. It's always returning a non-false value and that's why the loop never ends.
Why are you looping over the row data? Just return the query results-- they're already an array. If you want things like 'locationData' and 'userData' to be decoded JSON then use a model with methods to do this stuff for you. Remember, with MVC you should always put anything data related into models.
So a better way to do this is with Laravel models and relationships:
// put this with the rest of your models
// User.php
class User extends Model
{
function maps ()
{
return $this->hasMany ('App\Map');
}
}
// Maps.php
class Map extends Model
{
// you're not using this right now, but in case your view needs to get
// this stuff you can use these functions
function getLocationData ()
{
return json_decode ($this->locationData);
}
function getUserData ()
{
return json_decode ($this->userData);
}
}
// now in your controller:
public function dash ($id, Request $request) {
// $user should now be an instance of the User model
$user = JWTAuth::parseToken()->authenticate();
// don't use raw SQL if at all possible
//$q = DB::select('SELECT * FROM maps WHERE user_id = :id', ['id' => $id]);
// notice that User has a relationship to Maps defined!
// and it's a has-many relationship so maps() returns an array
// of Map models
$maps = $user->maps ();
return response()->json($maps);
}
You can loop over $q using a foreach:
foreach ($q as $row) {
// Do work here
}
See the Laravel docs for more information.
Synopsis
I have ran into a very confusing problem with laravel-5. Essentially what I have are a number of seeders which loads data from a JSON document and inserts into the database using eloquent - this works fine.
I have a final seeder which loops through some data, during this loop I load a record using but the moment I try to access the data I receive the following error:
[ErrorException]
array_merge(): Argument #1 is not an array
Seeder Snippet
foreach ($data as $country => $states) {
$countryId = Country::where('iso2_code', $country)->first()->pluck('id');
dd($countryId); // ErrorException
array_walk($states, function (&$value) use ($countryId) {
$value = ['country_id' => $countryId, 'name' => $value];
});
State::insert($states);
}
The confusing part about this error is if I do not use Eloquent my problem is solved, as per:
foreach ($data as $country => $states) {
$countryId = DB::table((new Country)->getTable())
->select('id')
->where('iso2_code', $country)
->first();
dd($countryId->id); // Works.
array_walk($states, function (&$value) use ($countryId) {
$value = ['country_id' => $countryId, 'name' => $value];
});
State::insert($states);
}
Why is it then that DB works as expected but Eloquent triggers some array_merge error?
The Solution
In my situation I was setting protected $dates = false on my model. Where typically as per the documentation I should be using public $timestamps = false or protected $dates = array();.
I am using MySQL as the database connection adapter for all my models. I have a downloads model and controller with an index function that renders either an HTML table or a CSV file depending on the type passed from the request. I also have a CSV media type to handle an array of data, which is working as expected (outputs array keys as headers then array values for each row of data).
I wish to do the same find query but then remove ID fields from the record set if a CSV file is going to be rendered. You'll notice that the download ID is being fetched even though it is not in the fields array, so simply changing the fields array based on the request type will not work.
I have tried the following in the index action of my downloads controller:
<?php
namespace app\controllers;
use app\models\Downloads;
class DownloadsController extends \lithium\action\Controller {
public function index() {
// Dynamic conditions
$conditions = array(...);
$downloads = Downloads::find('all', array(
'fields' => array('user_id', 'Surveys.name'),
'conditions' => $conditions,
'with' => 'Surveys',
'order' => array('created' => 'desc')
));
if ($this->request->params['type'] == 'csv') {
$downloads->each(function ($download) {
// THIS DOES NOT WORK
unset($download->id, $download->user_id);
// I HAVE TRIED THIS HERE AND THE ID FIELDS STILL EXIST
// var_dump($download->data());
// exit;
return $download;
});
return $this->render(array('csv' => $downloads->to('array')));
}
return compact('downloads');
}
}
?>
I thought there was an __unset() magic method on the entity object that would be called when you call the standard PHP unset() function on an entity's field.
It would be great if there was a $recordSet->removeField('field') function, but I can not find one.
Any help would be greatly appreciated.
Perhaps you should do $downloads = $downloads->to('array');, iterate the array with a for loop, remove those fields from each row, then return that array. If you have to do this same thing for a lot of actions, you could setup a custom Media handler that could alter the data without needing logic for it in your controller.
Take a look at this example in the Lithium Media class unit test.
You can also avoid having much logic for it in your controller at all through the use of a custom handler. This example also auto-generates a header row from the keys in your data.
In config/bootstrap/media.php:
Media::type('csv', 'application/csv', array(
'encode' => function($data, $handler, $response) {
$request = $handler['request'];
$privateKeys = null;
if ($request->privateKeys) {
$privateKeys = array_fill_keys($request->privateKeys, true);
}
// assuming your csv data is the first key in
// the template data and the first row keys names
// can be used as headers
$data = current($data);
$row = (array) current($data);
if ($privateKeys) {
$row = array_diff_key($row, $privateKeys);
}
$headers = array_keys($row);
ob_start();
$out = fopen('php://output', 'w');
fputcsv($out, $headers);
foreach ($data as $record) {
if (!is_array($record)) {
$record = (array) $record;
}
if ($privateKeys) {
$record = array_diff_key($record, $privateKeys);
}
fputcsv($out, $record);
}
fclose($out);
return ob_get_clean();
}
));
Your controller:
<?php
namespace app\controllers;
use app\models\Downloads;
class DownloadsController extends \lithium\action\Controller {
public function index() {
$this->request->privateKeys = array('id', 'user_id');
// Dynamic conditions
$conditions = array(...);
$downloads = Downloads::find('all', array(
'fields' => array('user_id', 'Surveys.name'),
'conditions' => $conditions,
'with' => 'Surveys',
'order' => array('created' => 'desc')
));
return compact('downloads');
}
}
?>
Why not then just dynamically set your $fields array?
public function index() {
$type = $this->request->params['type'];
//Exclude `user_id` if request type is CSV
$fields = $type == 'csv' ? array('Surveys.name') : array('user_id', 'Surveys.name');
$conditions = array(...);
$with = array('Surveys');
$order = array('created' => 'desc');
$downloads = Downloads::find('all', compact('conditions', 'fields', 'with', 'order'));
//Return different render type if CSV
return $type == 'csv' ? $this->render(array('csv' => $downloads->data())) : compact('downloads');
}
You can see in this example how I send the array for your CSV handler, otherwise it's the $downloads RecordSet object that goes to the view.