Associating CakePHP HABTM models without a view - php

I'm writing an import from CSV and have been able to successfully manually set data fields on a model in code by using the $this->Book->set() function and passing it a hash containing all of the fieldName => value pairings.
How do I create habtm associations in code? All the examples I've seen in the documentation are based on the $this->data returned from a form in the view. Because my data is coming from a csv file and not a view I can't use this!
So in the following example:
// Book habtm Tags
// Tag habtm Books
$this->Book->create();
$this->Book->set(
array(
'author' => 'tolkein',
'title' => 'lord of the rings')
);
$arrayOfTagIds = array(1, 5, 6);
// Do something with $arrayOfTagIds...
$this->Book->save();
How would I associate the $arrayOfTagIds with the Book?

Instead of using set to add data to the new entity and save to save it, use create and saveAll. In the example below, replace the BookTag model with the name of the model used in the relationship between books and tags. You'll also need to change tag_id to match the name of the field that represents the tag's id.
Example:
$book = array(
'Book' => array(
'author' => 'tolkein',
'title' => 'lord of the rings'
),
'Tag' => array(
'Tag' => array(1,5,6);
)
);
$this->Book->create($book);
$this->Book->saveAll();

Related

Print all values of an array in CDetailView Yii

I'm currently new to Yii and have some problems in my app.
I have 2 AR (Employee and Children) with the relations is Employee has_many Children. Now when we go into the view of Employee (in the view&id=blablabla not in index), I want to list all the children that the Employee have.
I have made a function in EmployeeController class to retrieve the children from my db
$anak = Anak::Model()->findAll(array(
'condition'=>'id_karyawan=:id_karyawan',
'params'=>array(':id_karyawan'=>$id_karyawan)));
//return $anak;
foreach ($anak as $data){
return $data->namaanak;
}
the problem is, it only showing 1 data (the first one, to be exact) of the children, even though in my db the Employee has 3 children. When I try to count of query result, it's showing 3.
My employee/view is like this
$this->widget('zii.widgets.CDetailView', array(
'data'=>$model,
'attributes'=>array(
'id_karyawan',
'nama',
array(
'name'=>'idjabatan',
'value'=>$model->findByPk($model->id_karyawan)->jabatan->namajabatan,
),
array(
'name'=>'gaji',
'value'=>Yii::app()->numberFormatter->formatCurrency($model->findByPk($model->id_karyawan)->jabatan->gaji, 'Rp'),
),
'alamat',
array(
'name'=>'ttl',
'value'=>$model->tempatlahir.', '.Yii::app()->dateFormatter->format("dd MMMM yyyy", $model->tgllahir),
),
array(
'name'=>'Istri',
'value'=>Istri::model()->getNama($model->id_karyawan),
'type'=>'raw'
),
array(
'name'=>'Anak',
'value'=>$this->getAnak($model->id_karyawan),
'type'=>'raw'
)
),
I have tried to google it, but i couldn't find any working answer :(
PS: karyawan = employee, anak = children.
You are only getting the first related value because you are returning it right away inside your foreach loop in the function. Gather all the related models first then return that and work with that data.
You can also skip that extra function inside your controller. If you use Gii, it should have already set up the proper Employee HAS_MANY children relation for you, so you could use that to pull all related models for each Employee model you are looking at. See below for a quick example
Employee model:
public function relations()
{
return array(
'children' => array(self::HAS_MANY, 'Child', 'employee_id'),
);
}
View file, passing an employee model to CDetailView:
$this->widget('zii.widgets.CDetailView', array(
'data' => $model,
'attributes'=>array(
array(
'name' => 'Children',
'value' => function ($data) {
// Get all related children using the relation defined in the Employee model and use CHtml::listData to store data inside the $children variable as an array using `id` as key and `child_name` as value
$children = CHtml::listData($data->children, 'id', 'child_name');
// Return names as a comma separated list
return implode(', ', $children);
},
'type'=>'raw'
)
)
);
My tables/column names probably don't match up with what you have but hopefully this helps.

Adding associated value by not using foreign key in CakePHP 2

Recently I've started using CakePHP by starting from using Bake to get simple CRUD.
I have one Model called "Contract" which is associated with "Product" and "Customer" as following.
public $belongsTo = array(
'Customer' => array(
'className' => 'Customer',
'foreignKey' => 'customer_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Now I wanted to customize the add view (add.ctp) created by Bake by not using select box as an input form for customer_id and product_id. Instead of using select box which shows all the possible name of product/customer that are associated with foreign key I want to use text field so that people can type in one of the field (for example product_code, customer_code) from Product / Customer table and convert it to foreign key to create a row for Contract table. In case there is a chance that no values be found from Customer / Product field I want to skip adding a row to Contract table.
I would like to know if this is possible.
Thank you,
The basic concept is this: Use a JS lib of your choice, guess it's jquery. Watch the text field for changes, on change make an ajax GET request with the value of the text field to a controller / action of your app that returns json. On click on one of the results set the id of the selected record to a hidden field. Done.
If you search Google or Stackoverflow you'll find plenty of examples.

how to set a relation with empty value?

I have 2 tables namely Equipment and Supply.
I have used array_merge in yii php to serve as union for two different tables for the purpose of diplaying them in a single grid.
Everything works fine with the fields that is common with the two tables. The problem is when I try to display a field that is only existing in one of these two tables. It says Property "Supply.equipType" is not defined" because only equipment has the equipType relation.
in my gridView:
array(
'name'=>'equipment_type',
'value'=>'$data->equipType->name',
),
in my controller where I did the merging:
$prov1 = new CActiveDataProvider('BaseEiEquipItem', array(
'criteria' => array(
'condition' => 'id>0'
)));
$prov2 = new CActiveDataProvider('BaseSiReceivedItem', array(
'criteria' => array(
'condition' => 'id>0'
)));
$records=array_merge($prov1->data , $prov2->data);
$provAll = new CArrayDataProvider($records,
array(
'sort' => array( //optional and sortring
'keyField'=>false,
'attributes' => array(
'id', 'description',),
),
'pagination' => array('pageSize' => 10) //optional add a pagination
)
);
$this->render('create',array(
'model'=>$model,
'searchModel'=>$searchModel,
'modelGrid'=>$modelGrid,
'provAll' => $provAll,
));
in my equipment model :
public function relations() {
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'equipType' => array(self::BELONGS_TO, 'BaseRefEquipmentType', 'equipment_type'),
);
}
Any idea on how to solve this? Is there any way to fake a relation or something?
thanks in advance
Just add condition to check if equipType is there:
'value'=>'isset($data->equipType) ? $data->equipType->name : ""'
Using sql to merge results would be better, though.
Can't you just use plain SQL union?
If Equipment has 2 fields (a, b) and Supply has 2 fields (b, c), you can do union like this:
SELECT a, b, null FROM Equipment
UNION ALL
SELECT null, b, c FROM Supply
This problem shows, that it's probably not a good idea to mix different model types in one gridview. If you have a model that has no equipType then it's pretty obvious, that you can't show this column in a gridview. So what would you expect?
As a (dirty) workaround you can add all missing columns as pseudo attributes to the models where they are missing:
public $equipType;

Yii create CButton Column for field that exists in another model

I'm trying to add the following functionality, however I'm not sure where to start. Any advice, examples, or direction would be greatly appreciated.
I want to add button to the cgridview of the main model in this context. Each of the records available in the cgridview for this model, have a unique attribute called lot for example R3XSEF9
There is another secondary table/model in my database that has records with this same lot attribute. However, this table only has certain records out of all the possible records, sometimes duplicates, and has a set of different attributes.
What I'd like to do is, using the lot attribute, for example lot R3XSEF9 from my cgridview, search the secondary table to see if there is one ore more corresponding rows which contains that same lot R3XSEF9.
If so, I would like the button to be displayed in my CButtonColumn and link to the views for those corresponding models of the secondary table. If not, I would like no button to appear.
Thanks for any help. If any clarification is required, I would be happy to do so.
First of all you need to link tables using "relations" function in model class. If you use FOREIGN KEY constraint in DB relations already filled.
SQL statement:
CREATE TABLE Model1
(
...
FOREIGN KEY(lot) REFERENCES MainModel(lot) ON UPDATE CASCADE ON DELETE RESTRICT,
...
)
Model class:
class MainModel extends CActiveRecord
{
...
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'lots' => array(self::HAS_MANY, 'Model2', 'lot'),
);
}
Then you can use custom button column in your grid (view file) like this:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id' => 'main-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
...
array(
'class' => 'CButtonColumn',
'template' => '{lots}',
'header' => 'Lots',
'buttons' => array(
'lots' => array(
'label' => 'Lots',
'imageUrl' => Yii::app()->request->baseUrl.'/img/....png',
'url' => 'Yii::app()->createUrl("controller1/lotlistbymainid", array("id" => $data->id))',
'visible' => 'count($data->lots) > 0',
),
),
),
Explanation of button parameters to be passed thru "buttons" array you can find here. Especialy this part:
buttons property
public array $buttons;
the configuration for additional buttons. Each array element specifies a single button which has the following format:
'buttonID' => array(
'label'=>'...', // text label of the button
'url'=>'...', // a PHP expression for generating the URL of the button
'imageUrl'=>'...', // image URL of the button. If not set or false, a text link is used
'options'=>array(...), // HTML options for the button tag
'click'=>'...', // a JS function to be invoked when the button is clicked
'visible'=>'...', // a PHP expression for determining whether the button is visible
)

Model Associations and Data Modelling

I am developing a web app for an art gallery, and I want to check I have set up the data model correctly.
I have a people table and an artists table. Some of the people are artists and some are not.
I also have a products table and each product belongs to one artist.
Below is a simplified version of the tables:
products
********
id
title
artist_id
artists
*******
id
profile
rating
person_id
people
******
id
first_name
last_name
I need to be able to retrieve the name of the artist when performing the find() method on the Product model.
Have I set up the data model correctly and if so what is the best way to retrieve the artist's name without getting lots of unwanted data?
It's possible to use CakePHP's bindModel & unbindModel to build up relations that don't rely on Cake's automagic goodness.
Since you were interested in doing the find on the Product model, I'll set out an example that can be called from the Products controller:
// unbind Artist
$this->Product->unbindModel(array(
'belongsTo' => array('Artist')
));
// bind Artist & Person using explicit conditions
$this->Product->bindModel(array(
'hasOne' => array(
'Artist' => array(
'foreignKey' => false,
'conditions' => array('Artist.id = Product.artist_id')
),
'Person' => array(
'foreignKey' => false,
'conditions' => array('Person.id = Artist.person_id')
),
)
));
// return the person
$person = $this->Product->find('first', array(
'conditions' => array(
'Product.id' => 1 // or any other condition
),
'fields' => array(
'Person.first_name',
'Person.last_name'
)
));
What's happening here?
Firstly, we unbind the Product model's relationship with the Artist model.
Secondly, we bind the Artist and Person models by explicitly defining the relationships and disabling Cake's automatic forigenKey connections.
Lastly, we do a 'first' find on the Product model and only request the 'first_name' & 'last_name' of the Person.
Each 'Product' belongs to an 'Artist' and each 'Artist' hasAndBelongsToMany 'Product'
You will also need the following table:
artists_products
****************
id
product_id
artist_id
http://book.cakephp.org/#!/view/1044/hasAndBelongsToMany-HABTM

Categories