I have a Document Model which represents a Document (name, description etc). I then have a DocumentData Model which represents the data of a Document. A Document has One to Many DocumentData,
So to create a Document I have a form. Lets say that my form has a text input for Document Owner. Within my DocumentData table, the label documentOwner is set as the key and the inputted data is set as the value. So with multiple form elements, my DocumentData table might look like this
document_data
id | documentId | key | value |
----------------------------------------------
1 | 1 | clientName | Google |
----------------------------------------------
2 | 1 | projectName | Analytics |
----------------------------------------------
3 | 1 | Contact | Mr Sharp |
----------------------------------------------
4 | 1 | startDate | 29/12/2016 |
----------------------------------------------
The Document name and description for the Document table is created using hidden fields within the view of the Document.
So I can create Documents without a problem. I am having a problem with my update function though. So far I have the following
public function update(Request $request, Project $project, $id)
{
$document = $project->document()->where('id', '=', $id)->first();
$docData = $document->documentData()->get();
$input = $request->all();
foreach($docData as $orgData) {
foreach($input as $key => $value) {
if($key !== "filePath" && $key !== "documentType" && $key !== "documentTypeDesc") {
if($key == $orgData->key) {
$orgData->value = $value;
$orgData->update();
}
}
}
}
return View::make($document->name.'Doc.edit', compact('project', 'document'));
}
So firstly I get the Document I am working on and store it in $document. I then get the DocumentData for this Document and store it in $docData. $docData is a collection containing my key-value pairings for a Document.
I then loop both the $docData and the inputted data and where the keys match, I reset its updated value.
However, at the moment, it updates everything to the last inputted data field. I am not sure where else I can perform the update operation, but I only need it to update the row it is referring too, not the whole thing.
How could I go about doing this?
Thanks
I've cleaned up the code a little, and made a few implementation changes. This code should work, so let us know if it doesn't, and if not, what failed.
public function update(Request $request, Project $project, $id)
{
// you can use eager loading and find on the relationship query
$document = $project->document()->with('documentData')->find($id);
// you can just access the relationship attribute, no need for the query
$docData = $document->documentData;
// gets all input, except the keys specified
$input = $request->except(['filePath', 'documentType', 'documentTypeDesc']);
foreach($docData as $orgData) {
// check if the input has a key that matches the docdata key
if (array_key_exists($orgData->key, $input)) {
// update the value
$orgData->value = $input[$orgData->key];
// use save, not update
$orgData->save();
}
}
return View::make($document->name.'Doc.edit', compact('project', 'document'));
}
Related
When I use get_field('repeater_name') it returns a string that contains the a number which is the count of the repeater while it should return array of sub-fields, this issue have been reported repeatedly before but none of them provided a stable fix to this, the only way is to edit the post and hit the update button and it will re-generate the repeater fields, but in my case, I have 19k posts and it's not possible to edit each post.
Also have_rows('repeater_name') returns false as expected and the real field data (get_post_meta($post_id, "field_{$field_key}", true) returns NULL
I would appreciate any help or any automated script that regenerates the repeater field keys and populate the values of all posts.
This issue is reported previously in here, here and here and none came with a real fix.
EDIT:
I spotted the problem, the problem is in wp_postmeta table, the wrong field keys are stored:
wp_postmeta table
| meta_key | meta_value |
| ------------- |:-------------:|
| _repeater_name | field_XXXXXXX |
| _repeater_0_subfield | field_ABCDEFG |
| _repeater_0_subfield2 | field_HIJKLMN |
The same field keys should match in wp_posts table, but they don't (maybe due to data migration or whatever reason):
| post_excerpt | post_name | post_type |
| ---------------| ------------- |-----------|
| repeater_name | field_YYYYYYY | acf-field |
| subfield | field_BBBBBBB | acf-field |
| subfield2 | field_CCCCCCC | acf-field |
I think if there's a way to sync field keys in the database, that will fix the problem
I made a solution for this, might not be the exact solution for this problem but it worked out for me, I wrote this fix to rebuild faulty fields, it will rebuild only fields that have this problem:
/**
* #param string $name
* #param string[] $keys
* #param int|null|\WP_Post $post
*/
function dw_build_repeater($name, $keys = [], $post = null) {
$post = get_post($post);
$repeater = get_field($name, $post->ID);
$arr = [];
if (is_array($repeater)) {
return $repeater;
}
if (!$repeater || !is_numeric($repeater)) {
return $arr;
}
for ($i = 0; $i < intval($repeater); $i++) {
foreach ($keys as $key) {
$arr[$i][$key] = get_post_meta($post->ID, $name . "_" . $i . "_{$key}");
}
}
update_field($name, $arr, $post->ID);
return $arr;
}
so whenever i want to call a repeater i simply do this:
if (
have_rows('repeater_name')
|| dw_build_repeater('repeater_name', ['subfield1', 'subfield2', 'subfield3'])
) {
while (have_rows('repeater_name')) {
the_row();
// And the rest
}
}
// OR simply with a foreach loop
foereach (dw_build_repeater('repeater_name', ['subfield1', 'subfield2', 'subfield3']) as $row) {
echo $row['subfield1'] ?? '';
}
I haven't tested it with nested repeaters but i suppose it might not be working with nested repeaters.
I've been having this issue when trying to access repeaters added to options pages through the PHP API. Specifically, in my case I was trying to get the repeater data during the acf/load_field filter. It didn't matter whether the hook was registered during init or acf_init.
I discovered that, at least in this case, you always have to call acf_enable_filter( 'local' ) before get_field() to get the data array instead of the item count.
I have get problem on this case, I don't know how to prevent multiple value in 3 column, I mean if the 3 column have multiple value it will not insert to database,for example I have Krs table where has data like this :
| id | nim | nip | kode_mk
| 1 | 134 | 154 | 543
and laravel will ignore this data when someone insert data like this :
nim=134,nip=154,kode_mk=543 laravel will not insert and give an
atttention
but laravel will accept if data like this :
nim=1132,nip=154,kode_mk=543 laravel will accept and save to database
nim=134,nip=1984,kode_mk=543 laravel will accept and save to database
nim=1345,nip=154,kode_mk=543 laravel will accept and save to database
I have a table like this where I want prevent the data in the black circle:
this my Krs.php :
protected $fillable = ['nim','nip','kode_mk','absen','uts','uas'];
protected $table = 'krs';
public function mahasiswas(){
return $this->belongsToMany('App\Mahasiswa','id');
}
public function dosens(){
return $this->belongsToMany('App\Dosen','id');
}
public function makuls(){
return $this->belongsToMany('App\Matakuliah','id');
}
this my KrsController.php :
public function store(KrsRequest $request)
{
$krs = new Krs([
'nim' => $request->get('nim'),
'nip' => $request->get('nip'),
'kode_mk' => $request->get('kode_mk'),
'absen' => $request->get('absen'),
'uts' => $request->get('uts'),
'uas' => $request->get('uas')
]);
if ($krs->save()) {
session()->flash('status','done_all');
session()->flash('pesan','Data berhasil disimpan');
}else{
session()->flash('status','clear');
session()->flash('pesan','Data gagal disimpan');
}
return redirect('Akademik/Krs/create');
}
I don't know how to explain it in english , sorry for my bad grammar
EDIT 1 Table Structure
In your Krs migration, set the field to require the value to be unique. For example:
$table->string('kode_mk')->unique();
I am trying to update each contract I have for a particular job entry. My condition is for every job I post, more than one applicant can be hired. Right now, this is what my code shows.
Job ID | Job Title | Provider | Contract Status | Provider's Feedback | Note to Self
1 | First job.| Provider1| DONE | |I like his work.
1 | First job.| Provider2| DONE | |I like his work.
This is supposedly correct but just until PROVIDER column. Whenever I click job title entry (I have it linked to another page where I can update the contract), I should be able to write something about the worker - if the job was done, how his performance was (which should be view-able on his equivalent page), and my note to self about him. Problem is, whenever I click one of the contracts and make an update, all the contracts will be affected. How will I make it unique?
Here's my code so far:
public function update_job_contract($post_obj)
{
$id = $post_obj['id'];
$data = array
(
'client_feedback' => $post_obj['client_feedback'],
'client_notetoself' => $post_obj['client_notetoself'],
'contract_status' => $post_obj['contract_status'],
'client_id' => $this->auth_model->get_user_id()
);
$this->db->where('id', $id);
$this->db->update('job', $data);
}
in my controller:
public function update_job_contract()
{
$this->validateRole('client');
$this->load->model('job_model');
$id = $this->uri->segment(3,0);
$data['job'] = $this->job_model->get_job($id);
$this->load->view('client/update_job_contract', $data);
}
public function update_job_contract_submit()
{
$this->validateRole('client');
$this->load->model('job_model');
//if ( '0' == $_POST['id'] ) {
$this->job_model->update_job_contract($_POST);
//}
redirect('client/manage_job_contracts?message=Congratulations!');
}
I am looking forward to getting help! Thanks!
I have two very similar classes. Lets say Class A and Class B.
+---------------+ +---------------+
| Class A | | Class B |
|---------------| |---------------|
| Name | | Name |
| ZIP | | ZIP |
| TelPhone | | TelPhone |
| | | MobilePhone |
+---------------+ +---------------+
I want to compare them in the values for all common attributes.
Here's a way I tried it, but doing it for all attributes (got more than only 3 attributes) looks like a overkill for me:
$differences = array();
if($classA->getName() != $classB->getName()) {
array_push(
$differences,
array('name' => array(
'classA' => $classA->getName(),
'classB' => $classB->getName()
)
));
}
// and the same code for every attribute....
What's the best approach here?
Additional to the handiwork, it is also not automatically updated if the classes are getting altered. For example if Class A gets also a MobilePhone attribute.
Please don't tell me, that I should do some Polymorphism, it's just an example to clarify.
I'm interested in the difference, so not only the attributes, also the values itself inside the attributes.
thanks
It's not the sexiest thing (because of the substring to getters.., but I can't go with properties, I think sym, but I got it:
// getting an array with all getter methods of a class
private function getGettersOf($object) {
$methodArray = array();
$reflection = new ReflectionClass($object);
$methods = $reflection->getMethods();
foreach ($methods as $method) {
// nur getter
if(strtolower(substr($method->getName(), 0, 3)) == "get") {
array_push($methodArray, $method->getName());
}
}
return $methodArray;
}
// getting an array with all differences
public function getDifferences($classA, $classB) {
// get list of attributes (getter methods) in commond
$classAMethods = $this->getGettersOf($classA);
$classBMethods = $this->getGettersOf($classB);
$intersection = array_intersect($classAMethods, $classBMethods);
$differences = array();
// iterate over all methods and check for the values of the reflected methods
foreach($intersection as $method) {
$refMethodClassB = new ReflectionMethod($classB, $method);
$refMethodClassA = new ReflectionMethod($classA, $method);
if($refMethodClassB->invoke($classB) != $refMethodClassA->invoke($classA)) {
array_push(
$differences,
array($method => array(
'classA' => $refMethodClassA->invoke($classA),
'classB' => $refMethodClassB->invoke($classB)
)
));
}
}
return $differences;
}
Special thanks to George Marques's comment.
i have a database which contains 11 tables 9 of which have a one to many relation with a User Information table.
User Table:
ID , Name , Age , phone , . .......
on basis of ID the relation is with tables for e.g
User_cars
ID,User_ID(fk),Make , Model , .....
What i need to be able to do is present this data in DB in a CSV file. Since the There is a one to many relation a simple join doesnt work as rows are duplicated.(Not presentable Format for the client :S)
Im using Yii as web app and need some extension which can present it in some readable way.
or maybe a php script can do the task as well.
You can handle it in one of two ways:
1. Use GROUP_CONCAT (as #Dave mentioned in the comment - this is the link: MYSQL PHP API - Displaying & Fetching multiple rows within rows from another table).
There is a downside however, GROUP_CONCAT has a limit (by default 1024 characters), which you can change with the server variable group_concat_max_len, but you may not have access to do so.
This will look pretty much like this:
ID | Name | Car Model | Year
1 John BMW, Audi, Renault 1999, 2000, 2003
2 David Mercedes, Ford 2000, 2005
This can get very complicated if there are 30-40 entries per user, and non-readable.
2. The second option is to export it in the following format (not hard to do, you just iterate through all the cars a user has, but write the user only in the first iteration).
ID | Name | Car Model | Year
1 John BMW 1999
Audi 2000
Renault 2003
2 David .....
Here's some sample code (I use plenty of made up methods, that have suggestive names).
$csvArray = array();
foreach ($users as $user) {
$cars = $user->getCars(); //random method name. Grabs an array with the cars this user has :)
$firstIteration = 1;
foreach ($cars as $car) {
if ($firstIteration == 1) { // we only set the CSV row the first time we iterate through cars
$csvSingle['ID'] = $user->getId(); // more random method names
$csvSingle['Name'] = $user->getName();
$firstIteration = 0;
}
$csvSingle['car_model'] = $car->getCarModel();
$csvSingle['car_year'] = $car->getCarYear();
$csvArray[] = $csvSingle; // now we add the single row to the big CSV array.
}
}
fputcsv($handle, $csvArray); // $handle is the file you export to
i like first variant by #"Vlad Preda" and in your controller or component for export you can use something like this:
get users with cars (and cars must fill in user model relations)
$users = User::model()->with('cars')->findAll();
and then
foreach ($users as $user) {
$csvOneUser = array('id'=>$user->id, "name"=>$user->name);
if (!$user->cars) {
$csvAll[] = $csvOneUser;
continue;
}
foreach ($user->cars as $car) {
$car_attributes = $car->getAttributes();
foreach ($car_attributes as $k=>$v) {
$csvOneUser[$k][] = $v;
}
}
}